首页 > 其他分享 >spring上 -基于注解配置bean,动态代理,AOP笔记

spring上 -基于注解配置bean,动态代理,AOP笔记

时间:2024-10-16 11:59:39浏览次数:1  
标签:hspedu java spring bean AOP import com public

 

用的是jdk8,spring框架里jar包的下载可以自己搜到

注解用到的jar包。

 

 

60,注解配置Bean快速入门

 

基本介绍

 

代码结构:

 

UserDao.java

package com.hspedu.spring.component;

import org.springframework.stereotype.Repository;

/*
* 使用 @Repository 标识该类是一个Repository,是一个持久层的类/对象
*/
@Repository
public class UserDao {
}

 

UserService.java

package com.hspedu.spring.component;

import org.springframework.stereotype.Service;

/*
 * 使用 @Service 标识该类是一个Service类/对象
 */
@Service
public class UserService {
}

 

UserAction.java

package com.hspedu.spring.component;

import org.springframework.stereotype.Controller;

/*
 * 使用 @Controller 标识该类是一个控制器Controller,通常这个类是一个Servlet
 */
@Controller
public class UserAction {
}

 

MyComponent.java

package com.hspedu.spring.component;

import org.springframework.stereotype.Component;

/*
 * 使用 @Component 标识该类是一个组件,是一个通用的注解
 */
@Component
public class MyComponent {
}

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <!--配置容器要扫描的包
    老师解读
    1. component-scan 要对指定包下的类进行扫描, 并创建对象到容器
    2. base-package 指定要扫描的包
    3. 含义是当spring容器创建/初始化时,就会扫描com.hspedu.spring.component包
       下的所有的 有注解 @Controller / @Service / @Respository / @Component类
       将其实例化,生成对象,放入到ioc容器
    4. resource-pattern="User*.class" 表示只扫描com.hspedu.spring.component 和它的子包下的User打头的类

    -->
    <context:component-scan base-package="com.hspedu.spring.component" />
</beans>

 

SpringBeanTest.java

package com.hspedu.spring.test;

import com.hspedu.spring.bean.*;
import com.hspedu.spring.component.MyComponent;
import com.hspedu.spring.component.UserAction;
import com.hspedu.spring.component.UserDao;
import com.hspedu.spring.component.UserService;
import com.hspedu.spring.factory.MyStaticFactory;
import com.hspedu.spring.service.MemberServiceImpl;
import com.hspedu.spring.web.OrderAction;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.awt.print.Book;

public class SpringBeanTest {

    //通过注解来配置Bean
    @Test
    public void setBeanByAnnotation() {
        ApplicationContext ioc =
                new ClassPathXmlApplicationContext("beans.xml");

        UserDao userDao = ioc.getBean(UserDao.class);
        UserService userService = ioc.getBean(UserService.class);
        UserAction userAction = ioc.getBean(UserAction.class);
        MyComponent myComponent = ioc.getBean(MyComponent.class);

        System.out.println("userDao=" + userDao);
        System.out.println("userService=" + userService);
        System.out.println("userAction=" + userAction);
        System.out.println("myComponent=" + myComponent);

        System.out.println("ok");

    }
}

 

运行结果:

 

63,注意事项和细节

 4,resource-pattern="User*.class" 表示只扫描com.hspedu.spring.component 和它的子包下的User打头的类,,这个和第3点自己记住就行。

 

还有其他3个注意事项都放在代码里了,就是 排除哪些注解 ,指定扫描哪些注解,标记注解后,可以使用什么来指定id值。

代码结构不变,beans.xml,UserDao.java,SpringBeanTest.java

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:contect="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <!--配置容器要扫描的包
    老师解读
    1. component-scan 要对指定包下的类进行扫描, 并创建对象到容器
    2. base-package 指定要扫描的包
    3. 含义是当spring容器创建/初始化时,就会扫描com.hspedu.spring.component包
       下的所有的 有注解 @Controller / @Service / @Respository / @Component类
       将其实例化,生成对象,放入到ioc容器
    4. resource-pattern="User*.class" 表示只扫描com.hspedu.spring.component 和它的子包下的User打头的类
    -->
<!--    <context:component-scan base-package="com.hspedu.spring.component"/>-->

    <!--
        需求:如果我们希望排除某个包/子包下的某种类型的注解,可以通过exclude-filter来指定
        1. context:exclude-filter 指定要排除哪些类
        2. type 指定排除方式 annotation表示按照注解来排除
        3. expression="org.springframework.stereotype.Service" 指定要排除的注解的全路径
    -->
<!--    <context:component-scan base-package="com.hspedu.spring.component">-->
<!--        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>-->
<!--    </context:component-scan>-->

    <!--
        需求:如果我们希望按照自己的规则,来扫描包/子包下的某些注解, 可以通过 include-filter
        1. use-default-filters="false" 表示不使用默认的过滤机制/扫描机制
        2. context:include-filter 表示要去扫描哪些类
        3. type="annotation" 按照注解方式来扫描/过滤
        4. expression="org.springframework.stereotype.Service" 指定要扫描的注解的全路径
    -->
    <context:component-scan base-package="com.hspedu.spring.component" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
    </context:component-scan>
</beans>

 

UserDao.java

package com.hspedu.spring.component;

import org.springframework.stereotype.Repository;

/*
* 使用 @Repository 标识该类是一个Repository,是一个持久层的类/对象
* 1,标记注解后,类名首字母小写作为id的值(默认)
* 2,value = "hspUserDao" 使用指定的 hspUserDao 作为 UserDao对象的 id
*/
@Repository(value = "hspUserDao")
public class UserDao {
}

 

SpringBeanTest.java

package com.hspedu.spring.test;

import com.hspedu.spring.bean.*;
import com.hspedu.spring.component.MyComponent;
import com.hspedu.spring.component.UserAction;
import com.hspedu.spring.component.UserDao;
import com.hspedu.spring.component.UserService;
import com.hspedu.spring.component.t.Pig;
import com.hspedu.spring.factory.MyStaticFactory;
import com.hspedu.spring.service.MemberServiceImpl;
import com.hspedu.spring.web.OrderAction;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.awt.print.Book;

public class SpringBeanTest {

    //通过注解来配置Bean
    @Test
    public void setBeanByAnnotation() {
        ApplicationContext ioc =
                new ClassPathXmlApplicationContext("beans.xml");

        UserDao userDao = ioc.getBean(UserDao.class);
        //默认情况:标记注解后,类名首字母小写作为 id 的值。也可以使用注解的 value 属性指定 id 值,并且 value 可以省略。
        System.out.println("userDao=" + userDao);

        UserService userService = ioc.getBean(UserService.class);
        UserAction userAction = ioc.getBean(UserAction.class);
        MyComponent myComponent = ioc.getBean(MyComponent.class);

        System.out.println("userService=" + userService);
        System.out.println("userAction=" + userAction);
        System.out.println("myComponent=" + myComponent);

        System.out.println("ok");

    }
}

 

Debug结果:

通过下断点,然后Debug来测试最后一个注意事项。 点 ioc -- beanFactory -- singletonObjects 就能看到

 

68,自己实现Spring注解配置Bean机制

 

 

思路分析:

 

 

代码结构:

其中 component 包里的4个java文件都是 上一篇笔记的代码,没有改变。

 

ComponentScan.java

package com.hspedu.spring.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @Target 用于修饰 Annotation定义,用于指定被修饰的 Annotation 能用于修饰哪些程序元素
 * @Retention 只能用于修饰一个 Annotation定义,用于指定该 Annotation可以保留多长时间
 * 1. @Target(ElementType.TYPE)指定我们的ComponentScan注解可以修饰 Type程序元素
 * 2. @Retention(RetentionPolicy.RUNTIME) 指定ComponentScan注解 保留范围:
      表示:编译器将把注解记录在class文件中,当运行Java程序时,通过反射获取该注解
 * 3. String value() default ""; 表示ComponentScan 可以传入 value
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
    String value() default "";
}

 

HspSpringConfig.java

package com.hspedu.spring.annotation;

/**
 * 这是一个配置类, 作用类似我们原生spring的 beans.xml 容器配置文件
 */
@ComponentScan(value = "com.hspedu.spring.component")
public class HspSpringConfig {
}

 

HspSpringApplicationContext.java

package com.hspedu.spring.annotation;

import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.File;
import java.lang.annotation.Annotation;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;

/**
 * HspSpringApplicationContext 类的作用类似Spring原生ioc容器
 */

public class HspSpringApplicationContext {
    private Class configClass;
    //ioc我存放的就是通过反射创建的对象(基于注解方式)
    private final ConcurrentHashMap<String, Object> ioc =
            new ConcurrentHashMap<>();

    //构造器
    public HspSpringApplicationContext(Class configClass) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        this.configClass = configClass;
        //System.out.println("this.configClass=" + this.configClass);
        //获取要扫描的包
        //1. 先得到HspSpringConfig配置的@ComponentScan(value = "com.hspedu.spring.component")
        ComponentScan componentScan =
                (ComponentScan)this.configClass.getDeclaredAnnotation(ComponentScan.class);
        //2. 通过componentScan的value=> 即要扫描的包
        String path = componentScan.value();
        System.out.println("要扫描的包= " + path);

        //得到要扫描的包下的所有资源(类 .class)
        //1.得到类的加载器
        ClassLoader classLoader =
                HspSpringApplicationContext.class.getClassLoader();
        //2. 通过类的加载器获取到要扫描的包的资源 url=》类似一个路径
        //一定要把. 替换成 /
        path = path.replace(".","/");
        URL resource =
                classLoader.getResource("com/hspedu/spring/component");
        System.out.println("resource=" + resource);

        //3. 将要加载的资源(.class) 路径下的文件进行遍历=>io
        File file = new File(resource.getFile());
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                System.out.println("=============");
                System.out.println("=" + f.getAbsolutePath());
                //D:\javaProjects\Spring\spring5\out\production\spring5\com\hspedu\spring\component\UserService.class
                //获取到 com.hspedu.spring.component.UserService
                String fileAbsolutePath = f.getAbsolutePath();
                //这里我们只处理.class文件
                if (fileAbsolutePath.endsWith(".class")) {
                    //1. 获取到类名
                    String className =
                            fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.indexOf(".class"));
                    //System.out.println("className=" + className);
                    //2. 获取类的完整的路径(全类名)
                    //老师解读 path.replace("/",".") => com.hspedu.spring.component.
                    String classFullName = path.replace("/", ".") + "." + className;
                    System.out.println("classFullName=" + classFullName);

                    //3. 判断该类是不是需要注入容器, 就看该类是不是有注解 @Component @Service..
                    //这时,我们就得到老该类的Class对象
                    //Class clazz = Class.forName(classFullName)
                    //老师说一下
                    //1. Class clazz = Class.forName(classFullName) 可以反射加载类
                    //2. classLoader.loadClass(classFullName); 可以反射类的Class
                    //3. 区别是 : 上面方式后调用来类的静态方法, 下面方法不会
                    Class<?> aClass = classLoader.loadClass(classFullName);
                    //4. aClass.isAnnotationPresent(Component.class) 判断该类是否有 @Component
                    if (aClass.isAnnotationPresent(Component.class) ||
                            aClass.isAnnotationPresent(Controller.class) ||
                            aClass.isAnnotationPresent(Service.class) ||
                            aClass.isAnnotationPresent(Repository.class)) {

                        //这里老师演示一个Component注解指定value,分配id
                        //老师就是演示了一下机制.

                        //这时就可以反射对象,并放入到容器中
                        Class<?> clazz = Class.forName(classFullName);
                        Object instance = clazz.newInstance();
                        //放入到容器中, 将类名的首字母小写作为id
                        //StringUtils
                        ioc.put(StringUtils.uncapitalize(className) , instance);
                    }
                }
            }
        }
    }

    //编写方法返回对容器中对象
    public Object getBean(String name) {
        return ioc.get(name);
    }
}

 

HspSpringApplicationContextTest.java

package com.hspedu.spring.annotation;

import com.hspedu.spring.component.MyComponent;
import com.hspedu.spring.component.UserAction;
import com.hspedu.spring.component.UserDao;
import com.hspedu.spring.component.UserService;


public class HspSpringApplicationContextTest {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        HspSpringApplicationContext ioc =
                new HspSpringApplicationContext(HspSpringConfig.class);

        UserAction userAction = (UserAction) ioc.getBean("userAction");
        System.out.println("userAction" + userAction);

        MyComponent myComponent = (MyComponent) ioc.getBean("myComponent");
        System.out.println("myComponent" + myComponent);

        UserService userService = (UserService) ioc.getBean("userService");
        System.out.println("userService=" + userService);

        UserDao userDao = (UserDao) ioc.getBean("userDao");
        System.out.println("userDao=" + userDao);

        System.out.println("ok");
    }
}

 

Debug结果:

 

运行结果:

 

76,自动装配@Autowired

代码结构:

 

UserService.java

package com.hspedu.spring.component;

import org.springframework.stereotype.Service;

/*
 * 使用 @Service 标识该类是一个Service类/对象
 */
@Service
public class UserService {
    public void hi() {
        System.out.println("UserService hi()~");
    }
}

 

UserAction.java

package com.hspedu.spring.component;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import sun.java2d.pipe.SpanIterator;

/*
 * 使用 @Controller 标识该类是一个控制器Controller,通常这个类是一个Servlet
 */
@Controller()
public class UserAction {
    //xml配置 ref
    //老师说明 @Autowired
    //1)在IOC容器中查找待装配的组件的类型,如果有唯一的bean匹配(按照类型),则使用该bean装配
    //2)如待装配的类型对应的bean在IOC容器中有多个,则使用待装配的属性的属性名作为id值再进行查找,
    //  找到就装配,找不到就抛异常
    @Autowired
    //private UserService userService;
    private UserService userService200;

    public void sayOk() {
//        System.out.println("UserAction.userService=" + userService);
//        System.out.println("userAction 装配的 userService属性=" + userService);
        System.out.println("userAction 装配的 userService200属性=" + userService200);
        //userService.hi();
    }
}

 

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:contect="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
   <context:component-scan
           base-package="com.hspedu.spring.component"/>

   <!--配置两个UserService对象-->
   <bean class="com.hspedu.spring.component.UserService" id="userService200"/>
   <bean class="com.hspedu.spring.component.UserService" id="userService300"/>
</beans>

 

SpringBeanTest.java

package com.hspedu.spring.test;

import com.hspedu.spring.bean.*;
import com.hspedu.spring.component.MyComponent;
import com.hspedu.spring.component.UserAction;
import com.hspedu.spring.component.UserDao;
import com.hspedu.spring.component.UserService;
import com.hspedu.spring.factory.MyStaticFactory;
import com.hspedu.spring.service.MemberServiceImpl;
import com.hspedu.spring.web.OrderAction;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.awt.print.Book;

public class SpringBeanTest {

    //通过注解来配置Bean
    @Test
    public void setProByAutowired() {
        ApplicationContext ioc =
                new ClassPathXmlApplicationContext("beans.xml");

        UserService userService = ioc.getBean("userService", UserService.class);
        System.out.println("ioc容器中的userService=" + userService);

        UserService userService200 = ioc.getBean("userService200", UserService.class);
        System.out.println("ioc容器中的userService200=" + userService200);

        UserAction userAction = ioc.getBean("userAction", UserAction.class);
        //System.out.println("ioc容器中的userAction=" + userAction);
        userAction.sayOk();

    }
}

 

运行结果:

 

80,自动装配@Resource

代码结构和上一节一样,UserAction.java, SpringBeanTest.java 改变了

UserAction.java

package com.hspedu.spring.component;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import sun.java2d.pipe.SpanIterator;

import javax.annotation.Resource;

/*
 * 使用 @Controller 标识该类是一个控制器Controller,通常这个类是一个Servlet
 */
@Controller()
public class UserAction {
    ///老师说明 @Resource
    //1) @Resource有两个属性是比较重要的,分是name和type,Spring将@Resource注解的name属性解析为bean的名字,
    //  而type属性则解析为bean的类型.所以如果使用name属性,则使用byName的自动注入策略,
    //  而使用type属性时则使用byType自动注入策略
    //  比如@Resource(name = "userService") 表示装配 id=userService对象
    //  比如@Resource(type = UserService.class) 表示按照UserService.class类型进行装配, 这时要求容器中,只能有一个这样类型的对象
    //2) 如果@Resource 没有指定 name 和 type ,则先使用byName注入策略,
    //   如果匹配不上, 再使用byType策略, 如果都不成功,就会报错

    //=================================
    //老师说明: @Autowired + @Qualifier(value = "userService02") 组合也可以完成指定 name/id 来进行自动装配
    //指定id进行组装, 也可以使用@Autowired 和 @Qualifier(value = "userService02")
    // 这时,是装配的 id=userService02 , 需要两个注解都需要写上,用@Resource(name="userSerice200") 更简单
//@Resource(name="userService200") @Resource(type = UserService.class) private UserService userService400; public void sayOk() { // System.out.println("UserAction.userService=" + userService); // System.out.println("userAction 装配的 userService属性=" + userService); System.out.println("userAction 装配的 userService属性=" + userService400); userService400.hi(); } }

 

SpringBeanTest.java

package com.hspedu.spring.test;

import com.hspedu.spring.bean.*;
import com.hspedu.spring.component.MyComponent;
import com.hspedu.spring.component.UserAction;
import com.hspedu.spring.component.UserDao;
import com.hspedu.spring.component.UserService;
import com.hspedu.spring.factory.MyStaticFactory;
import com.hspedu.spring.service.MemberServiceImpl;
import com.hspedu.spring.web.OrderAction;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.awt.print.Book;

public class SpringBeanTest {

    //通过注解来配置Bean
    @Test
    public void setProByAutowired() {
        ApplicationContext ioc =
                new ClassPathXmlApplicationContext("beans.xml");

        UserService userService = ioc.getBean("userService", UserService.class);
        System.out.println("ioc容器中的userService=" + userService);

//        UserService userService200 = ioc.getBean("userService200", UserService.class);
//        System.out.println("ioc容器中的userService200=" + userService200);

        UserAction userAction = ioc.getBean("userAction", UserAction.class);
        //System.out.println("ioc容器中的userAction=" + userAction);
        userAction.sayOk();

    }
}

 

运行结果:

 

82,泛型依赖注入

 

各个类的关系图:

 类关系图解释:

   如果 BookService 想用到 BookDao,可以把BookDao属性装配到BookService上去,但问题是 BaseService 和 BaseDao 下有很多子类,写起来就很麻烦,所以spring提供了泛型依赖注入,就是把 BaseDao装配到BaseService上去,只是形式上的装配,不会实例化他们的对象,真正实现的是子类对象,在得到BookService的时候,传入泛型<Book>,根据泛型依赖装配,会自动把BookDao对象装配到 BookService上去

底层机制 是 : 反射+注解+IO+String+泛型.

 

代码结构:

 

Book.java

package com.hspedu.spring.depinjection;

public class Book {
}

 

Phone.java

package com.hspedu.spring.depinjection;

public class Phone {
}

 

BaseDao.java

package com.hspedu.spring.depinjection;

//自定义泛型类
public abstract class BaseDao<T> {
    public abstract void save();
}

 

BookDao.java

package com.hspedu.spring.depinjection;

import org.springframework.stereotype.Repository;

@Repository
public class BookDao extends BaseDao<Book>{
    @Override
    public void save() {
        System.out.println("BookDao 的 save()..");
    }
}

 

PhoneDao.java

package com.hspedu.spring.depinjection;

import org.springframework.stereotype.Repository;

@Repository
public class PhoneDao extends BaseDao<Phone>{
    @Override
    public void save() {
        System.out.println("PhoneDao save()");
    }
}

 

BaseService.java

package com.hspedu.spring.depinjection;

import org.springframework.beans.factory.annotation.Autowired;

public class BaseService<T> {

    @Autowired
    private BaseDao<T> baseDao;

    public void save(){
        baseDao.save();
    }
}

 

BookService.java

package com.hspedu.spring.depinjection;

import org.springframework.stereotype.Service;

@Service
public class BookService extends BaseService<Book>{
    //并没有写属性
}

 

PhoneService.java

package com.hspedu.spring.depinjection;

import org.springframework.stereotype.Service;

@Service
public class PhoneService extends BaseService<Phone>{
    //是把PhoneDao对象装配到PhoneService,spring底层支持的
}

 

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:contect="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

   <context:component-scan
           base-package="com.hspedu.spring.depinjection"/>
</beans>

 

SpringBeanTest.java

package com.hspedu.spring.test;

import com.hspedu.spring.bean.*;
import com.hspedu.spring.component.MyComponent;
import com.hspedu.spring.component.UserAction;
import com.hspedu.spring.component.UserDao;
import com.hspedu.spring.component.UserService;
import com.hspedu.spring.depinjection.PhoneService;
import com.hspedu.spring.factory.MyStaticFactory;
import com.hspedu.spring.service.MemberServiceImpl;
import com.hspedu.spring.web.OrderAction;
import org.junit.Test;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.awt.print.Book;

public class SpringBeanTest {

    //通过泛型依赖来配置Bean
    @Test
    public void setProByDependencyInjection() throws BeansException {
        ApplicationContext ioc =
                new ClassPathXmlApplicationContext("beans.xml");
        PhoneService phoneService = ioc.getBean("phoneService", PhoneService.class);
        phoneService.save();

    }
}

 

运行结果:

 

86,传统方法解决动态代理需求

 

代码结构:

 

接口Vehicle.java

package com.hspedu.spring.proxy2;

//该接口有run方法
public interface Vehicle {
    public void run();
}

 

Car.java

package com.hspedu.spring.proxy2;

public class Car implements Vehicle{
    @Override
    public void run() {
        System.out.println("交通工具开始运行了...");
        System.out.println("小汽车在公路 running..");
        System.out.println("交通工具停止运行了...");
    }
}

 

Ship.java

package com.hspedu.spring.proxy2;

public class Ship implements Vehicle{
    @Override
    public void run() {
        System.out.println("交通工具开始运行了...");
        System.out.println("大轮船在水上 running...");
        System.out.println("交通工具停止运行了...");
    }
}

 

TestVehicle.java

package com.hspedu.spring.proxy2;

import org.junit.Test;

public class TestVehicle {
    @Test
    public void run(){
        //OOP基础
        Vehicle vehicle = new Car();
        vehicle.run();
        System.out.println("----------------");
        Vehicle vehicle1 = new Ship();
        vehicle1.run();
    }
}

 

运行结果:

 

如果想改某几个对象,只能一个一个的改 ,得不到统一的管理

 

87,动态代理解决需求

动态代理解决思路,在调用方法时,使用反射机制,根据方法去决定调用哪个对象方法

 

代码结构:

Vehicle.java不变,动态代理机制是 debug得出的

 

Car.java

package com.hspedu.spring.proxy2;

public class Car implements Vehicle{
    @Override
    public void run() {
        //System.out.println("交通工具开始运行了...");
        System.out.println("小汽车在公路 running..");
        //System.out.println("交通工具停止运行了...");
    }
}

 

Ship.java

package com.hspedu.spring.proxy2;

public class Ship implements Vehicle{
    @Override
    public void run() {
        //System.out.println("交通工具开始运行了...");
        System.out.println("大轮船在水上 running...");
        //System.out.println("交通工具停止运行了...");
    }
}

 

VehicleProxyProvider.java

package com.hspedu.spring.proxy2;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * VehicleProxyProvider 该类可以返回一个代理对象.
 */
public class VehicleProxyProvider {
    //定义一个属性
    //target_vehicle 表示真正要执行的对象
    //该对象实现了Vehicle接口
    private Vehicle target_vehicle;

    //构造器
    public VehicleProxyProvider(Vehicle target_vehicle) {
        this.target_vehicle = target_vehicle;
    }

    //编写一个方法,可以返回一个代理对象, 该代理对象可以通过反射机制调用到被代理对象的方法
    //老师解读
    //1. 这个方法非常重要, 理解有一定难度
    public Vehicle getProxy() {

        //得到类加载器
        ClassLoader classLoader =
                target_vehicle.getClass().getClassLoader();

        //得到要代理的对象/被执行对象 的接口信息,底层是通过接口来完成调用
        Class<?>[] interfaces = target_vehicle.getClass().getInterfaces();

        //创建InvocationHandler 对象
        //因为 InvocationHandler 是接口,所以我们可以通过匿名对象的方式来创建该对象

        InvocationHandler invocationHandler = new InvocationHandler() {
            /**
             * invoke 方法是将来执行我们的target_vehicle的方法时,会调用到
             * o: 表示代理对象
             * method: 就是通过代理对象调用方法时,的哪个方法 代理对象.run()
             * args: 表示调用 代理对象.run(xx) 传入的参数
             * return: 表示 代理对象.run(xx) 执行后的结果
             */
            @Override
            public Object invoke(Object o, Method method, Object[] args)
                    throws Throwable {
                System.out.println("交通工具开始运行了....");
                //这里是我们的反射基础 => OOP
                //method 是?: public abstract void com.hspedu.spring.proxy2.Vehicle.run()
                //target_vehicle 是? Ship对象
                //args 是null
                //这里通过反射+动态绑定机制,就会执行到被代理对象的方法
                //执行完毕就返回
                Object result = method.invoke(target_vehicle, args);
                System.out.println("交通工具停止运行了....");
                return result;
            }
        };
        /**

          public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
          老师解读
          1. Proxy.newProxyInstance() 可以返回一个代理对象
          2. ClassLoader loader: 类的加载器.
          3. Class<?>[] interfaces 就是将来要代理的对象的接口信息
          4. InvocationHandler h 调用处理器/对象 有一个非常重要的方法invoke
         */
        Vehicle proxy =
                (Vehicle) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
        return proxy;
    }
}

 

TestVehicle.java

package com.hspedu.spring.proxy2;

import org.junit.Test;

public class TestVehicle {
    @Test
    public void run(){
        //OOP基础
        Vehicle vehicle = new Car();

        //创建VehicleProxyProvider对象, 并且我们传入的要代理的对象
        VehicleProxyProvider vehicleProxyProvider =
                new VehicleProxyProvider(vehicle);

        //获取代理对象, 该对象可以代理执行方法
        //老师解读
        //1. porxy 编译类型 Vehicle
        //2. 运行类型 是代理类型 class com.sun.proxy.$Proxy9
        Vehicle proxy = vehicleProxyProvider.getProxy();
        System.out.println("proxy的编译类型是 Vehicle");
        System.out.println("proxy的运行类型是 " + proxy.getClass());
        //下面老韩就要给大家解读/debug怎么 执行到 代理对象的 public Object invoke(Object o, Method method, Object[] args)
        //梳理完毕. proxy的编译类型是 Vehicle, 运行类型是 class com.sun.proxy.$Proxy9
        //所以当执行run方法时,会执行到 代理对象的invoke

        //如何体现动态 [1. 被代理的对象 2. 方法]
        //1. proxy 运行类型是 com.sun.proxy.$Proxy0 该类型被转型成 Vehicle
        // 因此可以调用 Vehicle 的接口方法
        //2. 当执行 run() 的时候会调用, 根据 Java 的动态绑定机制, 这时直接调用 Car的 run(),
        // 而是 proxy 对象的 invocationHandler 的 invoke 方法(!!!!!!)
        //3. invoke 方法使用反射机制来调用 run()方法注意这个 run 方法也可以是Vehicle 的其它方法)
        // 这时就可以在调用 run()方法前,进行前置处理和后置处理
        //4. 也就是说 proxy 的 target_vehicle 运行类型只要是实现了 Vehicle 接口
        // ,就可以去调用不同的方法, 是动态的,变化的,底层就是 使用反射完成的.
        proxy.run();
    }
}

 

运行结果:

 

91,动态代理深入(传统方法解决)

 

传统的解决思路,在各个方法的[前,执行过程, ]输出日志。

 

代码结构:

 

SmartAnimalable.java

package com.hspedu.spring.aop.proxy;

//接口
public interface SmartAnimalable {

    //求和
    float getSum(float i, float j);

    //求差
    float getSub(float i, float j);
}

 

SmartDog.java

package com.hspedu.spring.aop.proxy;

public class SmartDog implements SmartAnimalable{
    @Override
    public float getSum(float i, float j) {
        System.out.println("日志-方法名-getSum-参数 " + i + " " + j);
        float result = i + j;
        System.out.println("方法内部打印result = " + result);
        System.out.println("日志-方法名-getSum-结果result= " + result);
        return result;
    }

    @Override
    public float getSub(float i, float j) {
        System.out.println("日志-方法名-getSub-参数 " + i + " " + j);
        float result = i - j;
        System.out.println("方法内部打印result = " + result);
        System.out.println("日志-方法名-getSub-结果result= " + result);
        return result;
    }
}

 

AopTest.java

package com.hspedu.spring.aop.proxy;

import org.junit.Test;

public class AopTest {
    @Test
    public void smartDogTest() {
        SmartAnimalable smartAnimalable = new SmartDog();
        smartAnimalable.getSum(10, 2);
        System.out.println("===========================");
        smartAnimalable.getSub(10, 2);
    }
}

 

运行结果:

 

 

93,动态代理深入(动态代理方式解决)

代码结构:

SmartAnimalable.java不变

 

SmartDog.java

package com.hspedu.spring.aop.proxy;

public class SmartDog implements SmartAnimalable{
    @Override
    public float getSum(float i, float j) {
        //System.out.println("日志-方法名-getSum-参数 " + i + " " + j);
        float result = i + j;
        System.out.println("方法内部打印result = " + result);
        //System.out.println("日志-方法名-getSum-结果result= " + result);
        return result;
    }

    @Override
    public float getSub(float i, float j) {
        //System.out.println("日志-方法名-getSub-参数 " + i + " " + j);
        float result = i - j;
        System.out.println("方法内部打印result = " + result);
        //System.out.println("日志-方法名-getSub-结果result= " + result);
        return result;
    }
}

 

MyProxyProvider.java

package com.hspedu.spring.aop.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

/**
 * 可以返回一个动态代理对象, 可以执行SmartDog对象的方法
 */
public class MyProxyProvider {
    //定义我们要执行的目标对象, 该对象需要实现SmartAnimalable
    private SmartAnimalable target_obj;

    //构造器
    public MyProxyProvider(SmartAnimalable target_obj) {
        this.target_obj = target_obj;
    }

    //方法, 可以返回代理对象,该代理对象可以执行目标对象
    public SmartAnimalable getProxy() {

        //1. 先到的类加载器/对象
        ClassLoader classLoader = target_obj.getClass().getClassLoader();

        //2. 得到要执行的目标对象的接口信息
        Class<?>[] interfaces = target_obj.getClass().getInterfaces();

        //3. 创建InvocationHandler
        InvocationHandler invocationHandler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object result = null;
                try {
                    System.out.println("方法执行前-日志-方法名-" + method.getName() + "-参数 "
                            + Arrays.asList(args)); //这里从AOP看,就是一个横切关注点-前置通知
                    //使用反射调用方法
                    result = method.invoke(target_obj, args);
                    System.out.println("方法执行正常结束-日志-方法名-" + method.getName() + "-结果result= "
                            + result);//从AOP看, 也是一个横切关注点-返回通知

                } catch (Exception e) {
                    e.printStackTrace();
                    //如果反射执行方法时,出现异常,就会进入到catch{}
                    System.out.println("方法执行异常-日志-方法名-" + method.getName()
                            + "-异常类型=" + e.getClass().getName());//从AOP看, 也是一个横切关注点-异常通知
                } finally {//不管你是否出现异常,最终都会执行到finally{}
                    //从AOP的角度看, 也是一个横切关注点-最终通知
                    System.out.println("方法最终结束-日志-方法名-" + method.getName());
                }

                return result;
            }
        };
        //创建代理对象
        SmartAnimalable proxy =
                (SmartAnimalable) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
        return proxy;
    }
}

 

AopTest.java

package com.hspedu.spring.aop.proxy;

import org.junit.Test;

public class AopTest {
    @Test
    public void smartDogTestByProxy() {
        SmartAnimalable smartAnimalable = new SmartDog();

        //创建MyProxyProvider对象, 并且我们传入的要代理的对象
        MyProxyProvider myProxyProvider =
                new MyProxyProvider(smartAnimalable);

        //获取代理对象, 该对象可以代理执行方法
        SmartAnimalable proxy =
                myProxyProvider.getProxy();

        proxy.getSum(10, 2);
        System.out.println("====================");
        proxy.getSub(10, 2);
    }
}

 

运行结果:

 

95,AOP快速入门

基本介绍

可以把切面类里的方法(想象成一把刀)切入到A类,B类任意一个位置

 

 

 

 

案例:

 

 

代码结构:

 

SmartAnimalable.java

package com.hspedu.spring.aop.aspectj;

//接口
public interface SmartAnimalable {
    //求和
    float getSum(float i, float j);
    //求差
    float getSub(float i, float j);
}

 

SmartDog.java

package com.hspedu.spring.aop.aspectj;

import org.springframework.stereotype.Component;

@Component //使用@Component 当spring容器启动时,将 SmartDog注入到容器
public class SmartDog implements SmartAnimalable {
    @Override
    public float getSum(float i, float j) {
        float result = i + j;
        //int res = 9 / 0; //模拟一个算术异常
        System.out.println("方法内部打印result = " + result);
        return result;
    }

    @Override
    public float getSub(float i, float j) {
        float result = i - j;
        System.out.println("方法内部打印result = " + result);
        return result;
    }
}

 

SmartAnimalAspect.java

package com.hspedu.spring.aop.aspectj;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * 切面类 , 类似于我们以前自己写的MyProxyProvider,但是功能强大很多
 */
//@Order(value = 2)//表示该切面类执行的顺序, value的值越小, 优先级越高
@Aspect //表示是一个切面类[底层切面编程的支撑(动态代理+反射+动态绑定...)]
@Component //会注入SmartAnimalAspect到容器
public class SmartAnimalAspect {

    //定义一个切入点, 在后面使用时可以直接引用, 提高了复用性
    @Pointcut(value = "execution(public float com.hspedu.spring.aop.aspectj.SmartDog.getSum(float, float)))")
    public void myPointCut() {
    }

    /**
     * 老师解读
     * 1. @Before 表示前置通知:即在我们的目标对象执行方法前执行
     * 2. value = "execution(public float com.hspedu.spring.aop.aspectj.SmartDog.getSum(float, float)
     * 指定切入到哪个类的哪个方法  形式是: 访问修饰符 返回类型 全类名.方法名(形参列表)
     * 3. showBeginLog方法可以理解成就是一个切入方法, 这个方法名是可以程序员指定  比如:showBeginLog
     * 4. JoinPoint joinPoint 在底层执行时,由AspectJ切面框架, 会给该切入方法传入 joinPoint对象
     * , 通过该方法,程序员可以获取到 相关信息
     *
     * @param joinPoint
     */

    //希望将f1方法切入到SmartDog-getSum前执行-前置通知
    //@Before(value = "execution(public float com.hspedu.spring.aop.aspectj.SmartDog.getSum(float, float))")
    //这里我们使用定义好的切入点
    @Before(value = "myPointCut()")
    public void showBeginLog(JoinPoint joinPoint) {
        //通过连接点对象joinPoint 可以获取方法签名
        Signature signature = joinPoint.getSignature();
        System.out.println("SmartAnimalAspect-切面类showBeginLog()[使用的myPointCut()]-方法执行前-日志-方法名-" + signature.getName() + "-参数 "
                + Arrays.asList(joinPoint.getArgs()));
    }

    //返回通知:即把showSuccessEndLog方法切入到目标对象方法正常执行完毕后的地方
    //老韩解读
    //1. 如果我们希望把目标方法执行的结果,返回给切入方法
    //2. 可以再 @AfterReturning 增加属性 , 比如 returning = "res"
    //3. 同时在切入方法增加 Object res
    //4. 注意: returning = "res" 和 Object res 的 res名字一致
    //@AfterReturning(value = "execution(public float com.hspedu.spring.aop.aspectj.SmartDog.getSum(float, float))", returning = "res")
    //使用切入点
    @AfterReturning(value = "myPointCut()", returning = "res")
    public void showSuccessEndLog(JoinPoint joinPoint, Object res) {
        Signature signature = joinPoint.getSignature();
        System.out.println("SmartAnimalAspect-切面类showSuccessEndLog()-方法执行正常结束-日志-方法名-" + signature.getName() + " 返回的结果是=" + res);
    }


    //异常通知:即把showExceptionLog方法切入到目标对象方法执行发生异常的的catch{}
    //@AfterThrowing(value = "execution(public float com.hspedu.spring.aop.aspectj.SmartDog.getSum(float, float))", throwing = "throwable")
    //直接使用切入点表达式
    @AfterThrowing(value = "myPointCut()", throwing = "throwable")
    public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {
        Signature signature = joinPoint.getSignature();
        System.out.println("SmartAnimalAspect-切面类showExceptionLog()-方法执行异常-日志-方法名-" + signature.getName() + " 异常信息=" + throwable);
    }

    //最终通知:即把showFinallyEndLog方法切入到目标方法执行后(不管是否发生异常,都要执行 finally{})
    //@After(value = "execution(public float com.hspedu.spring.aop.aspectj.SmartDog.getSum(float, float))")
    //直接使用切入点
    @After(value = "myPointCut()")
    public void showFinallyEndLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        System.out.println("SmartAnimalAspect-切面类showFinallyEndLog()-方法最终执行完毕-日志-方法名-" + signature.getName());
    }
}

 

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

   <context:component-scan
           base-package="com.hspedu.spring.aop.aspectj"/>

   <!-- 开启基于注解的AOP功能 -->
   <aop:aspectj-autoproxy/>
</beans>

 

AopAspectjTest.java

package com.hspedu.spring.aop.aspectj;

import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AopAspectjTest {
    @Test
    public void smartDogTestByProxy(){
        //得到spring容器
        ApplicationContext ioc =
                new ClassPathXmlApplicationContext("beans.xml");

        //这里我们需要通过接口类型来获取到注入的SmartDog对象-就是代理对象
        SmartAnimalable smartAnimalable =
                ioc.getBean(SmartAnimalable.class);

        smartAnimalable.getSum(10, 2);
//        System.out.println("smartAnimalable运行类型="
//                + smartAnimalable.getClass());//class com.sun.proxy.$Proxy17
    }
}

 

运行结果:

 

代码里的getBean() 也相当于 动态代理里的 getProxy() ,返回的是代理对象。

 

108,课后作业

 

这个题用到了细节5 和 下节的 切入表达式。

细节:

 

代码结构:

beans.xml 不变,和上节一样。

 

接口UsbInterface.java

package com.hspedu.spring.aop.aspectj;

public interface UsbInterface {
    public void work();
}

Phone.java

package com.hspedu.spring.aop.aspectj;

import org.springframework.stereotype.Component;

@Component//将Phone对象当做一个组件注入容器
public class Phone implements UsbInterface{
    @Override
    public void work() {
        System.out.println("手机开始工作了...");
    }

}

 

Camera.java

package com.hspedu.spring.aop.aspectj;

import org.springframework.stereotype.Component;

@Component//将Camera对象当做一个组件注入容器
public class Camera implements UsbInterface{
    @Override
    public void work() {
        System.out.println("相机开始工作了...");
    }
}

 

SmartAnimalAspect.java

package com.hspedu.spring.aop.aspectj;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * 切面类 , 类似于我们以前自己写的MyProxyProvider,但是功能强大很多
 */
//@Order(value = 2)//表示该切面类执行的顺序, value的值越小, 优先级越高
@Aspect //表示是一个切面类[底层切面编程的支撑(动态代理+反射+动态绑定...)]
@Component //会注入SmartAnimalAspect到容器
public class SmartAnimalAspect {

    @Before(value = "execution(public void com.hspedu.spring.aop.aspectj.Phone.work()) || execution(public void com.hspedu.spring.aop.aspectj.Camera.work())")
    public void hi(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        System.out.println("切面类的hi()-执行的目标方法-" + signature.getName());
    }
}

 

AopAspectjTest.java

package com.hspedu.spring.aop.aspectj;

import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AopAspectjTest {
    @Test
    public void smartDogTestByProxy(){
        //得到spring容器
        ApplicationContext ioc =
                new ClassPathXmlApplicationContext("beans.xml");

        //这里我们不能通过接口类型来获取到注入的Phone对象-就是代理对象,因为Phone对象和Camera对象都实现了UsbInterface接口
//        UsbInterface bean = ioc.getBean(UsbInterface.class);
//        bean.work();

        UsbInterface phone = (UsbInterface) ioc.getBean("phone");
        UsbInterface camera = (UsbInterface) ioc.getBean("camera");

        phone.work();
        System.out.println("---------------------");
        camera.work();
    }
}

 

运行结果:

 

109,切入表达式

具体使用:

 

细节:

细节3代码结构:

beans.xml和上上一节一样。

 

Car.java

package com.hspedu.spring.aop.aspectj;

import org.springframework.stereotype.Component;

@Component//将Phone对象当做一个组件注入容器
public class Car {
    public void run(){
        System.out.println("小汽车在running...");
    }
}

 

SmartAnimalAspect.java

package com.hspedu.spring.aop.aspectj;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * 切面类 , 类似于我们以前自己写的MyProxyProvider,但是功能强大很多
 */
//@Order(value = 2)//表示该切面类执行的顺序, value的值越小, 优先级越高
@Aspect //表示是一个切面类[底层切面编程的支撑(动态代理+反射+动态绑定...)]
@Component //会注入SmartAnimalAspect到容器
public class SmartAnimalAspect {

    //给Car配置一个前置通知
    @Before(value = "execution(public void com.hspedu.spring.aop.aspectj.Car.run())")
    public void ok1(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        System.out.println("切面类的ok1()-执行的目标方法-" + signature.getName());
    }
}

 

AopAspectjTest.java

package com.hspedu.spring.aop.aspectj;

import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AopAspectjTest {
    @Test
    public void smartDogTestByProxy(){
        //得到spring容器
        ApplicationContext ioc =
                new ClassPathXmlApplicationContext("beans.xml");

        //Car对象仍然是代理对象
        Car car = ioc.getBean(Car.class);
        car.run();
        System.out.println("car的运行类型=" + car.getClass());
    }
}

 

运行结果:

 

115,环绕通知

 

在第 95节代码的基础上修改 ,可以把 SmartAnimalAspect.java删了,重新复制成 SmartAnimalAspect2.java,还要修改 AopAspectjTest.java

 

SmartAnimalAspect2.java

package com.hspedu.spring.aop.aspectj;


import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;

/**
 * @author 韩顺平
 * @version 1.0
 * 切面类 , 类似于我们以前自己写的MyProxyProvider,但是功能强大很多
 */
@Aspect //表示是一个切面类[底层切面编程的支撑(动态代理+反射+动态绑定...)]
@Component //会注入SmartAnimalAspect2到容器
public class SmartAnimalAspect2 {

    //演示环绕通知的使用-了解
    //老师解读
    //1. @Around: 表示这是一个环绕通知[完成其它四个通知的功能]
    //2. value = "execution(public float com.hspedu.spring.aop.aspectj.SmartDog.getSum(float, float)) 切入点表达式
    //3. doAround 表示要切入的方法 - 调用结构 try-catch-finally
    @Around(value = "execution(public float com.hspedu.spring.aop.aspectj.SmartDog.getSum(float, float))")
    public Object doAround(ProceedingJoinPoint joinPoint) {
        Object result = null;
        String methodName = joinPoint.getSignature().getName();
        try {
            //1.相当于前置通知完成的事情
            Object[] args = joinPoint.getArgs();
            List<Object> argList = Arrays.asList(args);
            System.out.println("AOP环绕通知[-前置通知]" + methodName + "方法开始了--参数有:" + argList);
            //在环绕通知中一定要调用joinPoint.proceed()来执行目标方法
            result = joinPoint.proceed();
            //2.相当于返回通知完成的事情
            System.out.println("AOP环绕通知[-返回通知]" + methodName + "方法结束了--结果是:" + result);
        } catch (Throwable throwable) {
            //3.相当于异常通知完成的事情
            System.out.println("AOP环绕通知[-异常通知]" + methodName + "方法抛异常了--异常对象:" + throwable);
        } finally {
            //4.相当于最终通知完成的事情
            System.out.println("AOP环绕通知[-后置通知]" + methodName + "方法最终结束了...");
        }
        return result;
    }
}

 

AopAspectjTest.java

package com.hspedu.spring.aop.aspectj;

import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AopAspectjTest {
    @Test
    public void testDoAround() {
        //得到spring容器
        ApplicationContext ioc =
                new ClassPathXmlApplicationContext("beans.xml");


        SmartAnimalable smartAnimalable =
                ioc.getBean(SmartAnimalable.class);

        smartAnimalable.getSum(10, 2);
    }
}

 

运行结果:

 

119,基于XML配置的AOP

 

代码结构:

SmartAnimalable.java 和第95节的一样,就是要注意 import 那引入的包名。

SmartDog.java 把@Component 注解注释了。

 

SmartAnimalAspect.java

package com.hspedu.spring.aop.xml;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Before;

import java.util.Arrays;

/**
 * @author 韩顺平
 * @version 1.0
 *
 * 这是我们开发一个切面类, 但是不用注解,而是使用XML配置
 */
public class SmartAnimalAspect {
    public void showBeginLog(JoinPoint joinPoint) {
        //通过连接点对象joinPoint 可以获取方法签名
        Signature signature = joinPoint.getSignature();
        System.out.println("SmartAnimalAspect-XML配置-切面类showBeginLog()[使用的myPointCut()]-方法执行前-日志-方法名-" + signature.getName() + "-参数 "
                + Arrays.asList(joinPoint.getArgs()));
    }

    public void showSuccessEndLog(JoinPoint joinPoint, Object res) {
        Signature signature = joinPoint.getSignature();
        System.out.println("SmartAnimalAspect-XML配置-切面类showSuccessEndLog()-方法执行正常结束-日志-方法名-" + signature.getName() + " 返回的结果是=" + res);
    }


    public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {
        Signature signature = joinPoint.getSignature();
        System.out.println("SmartAnimalAspect-XML配置-切面类showExceptionLog()-方法执行异常-日志-方法名-" + signature.getName() + " 异常信息=" + throwable);
    }

    public void showFinallyEndLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        System.out.println("SmartAnimalAspect-XML配置-切面类showFinallyEndLog()-方法最终执行完毕-日志-方法名-" + signature.getName());
    }
}

 

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

   <!--使用XML配置,完成AOP编程-->
   <!--配置一个切面类对象-bean-->
   <bean class="com.hspedu.spring.aop.xml.SmartAnimalAspect" id="smartAnimalAspect"/>

   <!--配置一个SmartDog对象-bean-->
   <bean class="com.hspedu.spring.aop.xml.SmartDog" id="smartDog"/>

   <!--配置切面类, 细节一定要引入 xmlns:aop-->
   <aop:config>
      <!--配置切入点-->
      <aop:pointcut id="myPointCut" expression="execution(public float com.hspedu.spring.aop.xml.SmartDog.getSum(float, float)))"/>

      <!--这里指定切面对象,切面的前置,返回, 异常, 最终通知-->
      <aop:aspect ref="smartAnimalAspect" order="10">
         <!--配置前置通知-->
         <aop:before method="showBeginLog" pointcut-ref="myPointCut"/>
         <!--返回通知-->
         <aop:after-returning method="showSuccessEndLog" pointcut-ref="myPointCut" returning="res"/>
         <!--异常通知-->
         <aop:after-throwing method="showExceptionLog" pointcut-ref="myPointCut" throwing="throwable"/>
         <!--最终通知-->
         <aop:after method="showFinallyEndLog" pointcut-ref="myPointCut"/>
         <!--配置环绕通知-->
         <!--<aop:around method=""/>-->
      </aop:aspect>
   </aop:config>
</beans>

 

AopAspectjXMLTest.java

package com.hspedu.spring.aop.xml;

import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AopAspectjXMLTest {
    @Test
    public void testDoAround() {
        //得到spring容器
        ApplicationContext ioc =
                new ClassPathXmlApplicationContext("beans.xml");
        SmartAnimalable smartAnimalable =
                ioc.getBean(SmartAnimalable.class);
        smartAnimalable.getSum(10, 2);
    }
}

 

运行结果:

 

123,作业(注解方式)

 

代码结构:

 

Cal.java

package com.hspedu.spring.aop.homework;

public interface Cal {
    public int cal1(int n);
    public int cal2(int n);
}

 

MyCal.java

package com.hspedu.spring.aop.homework;

import org.springframework.stereotype.Component;

@Component//将MyCal对象当做一个组件注入容器
public class MyCal implements Cal{
    @Override
    public int cal1(int n) {
        int res = 1;
        for(int i = 1; i <= n; i++) {
            res += i;
        }
        System.out.println("cal1 执行结果=" + res);
        return res;
    }

    @Override
    public int cal2(int n) {
        int res = 1;
        for(int i = 1; i <= n; i++) {
            res *= i;
        }
        System.out.println("cal2 执行结果=" + res);
        return res;
    }
}

 

MyCalAop.java

package com.hspedu.spring.aop.homework;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect //MyCalAOP 是一个切面类
@Component //MyCalAOP/对象 作为组件注入到spring容器
public class MyCalAop {

    //前置通知
    //这里注意,如果目标类和切面类,在同一个包,可以省略包名
    //因为cal1和cal2方法,都要去输出开始执行时间,因此使用MyCal.*
    @Before(value = "execution(public int MyCal.*(int))")
    public void calStart(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        System.out.println(signature.getName() + " 执行, 开始执行时间=" + System.currentTimeMillis());
    }

    //返回通知
    //这里注意,如果目标类和切面类,在同一个包,可以省略包名
    //因为cal1和cal2方法,都要去输出开始执行时间,因此使用MyCal.*
    @AfterReturning(value = "execution(public int MyCal.*(int))")
    public void calEnd(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        System.out.println(signature.getName() + " 执行, 结束时间=" + System.currentTimeMillis());

    }
}

 

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

   <!--扫描指定包-->
   <context:component-scan
           base-package="com.hspedu.spring.aop.homework"/>

   <!--启用基于注解的AOP功能-->
   <aop:aspectj-autoproxy/>
</beans>

 

TestMyCalAOP.java

package com.hspedu.spring.aop.homework;

import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestMyCalAOP {
    @Test
    public void testMyCalByAnnotation() {

        //得到spring容器
        ApplicationContext ioc =
                new ClassPathXmlApplicationContext("beans.xml");

        Cal cal = ioc.getBean(Cal.class);

        cal.cal1(10);
        System.out.println("-------------------------");
        cal.cal2(5);
    }
}

 

运行结果:

 

124,作业(基于XML)

代码结构:

Cal.java,MyCal.java , TestMyCalAOP.java和上节一样,要注意引入的包名。

 

MyCalAOP.java

package com.hspedu.spring.aop.homework.xml;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;


public class MyCalAOP {

    //前置通知
    public void calStart(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        System.out.println(signature.getName() + " 基于XML配置- 执行, 开始执行时间=" + System.currentTimeMillis());

    }

    //返回通知
    public void calEnd(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        System.out.println(signature.getName() + " 基于XML配置- 执行, 结束时间=" + System.currentTimeMillis());

    }
}

 

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

   <!--配置MyCalAOP-bean-->
   <bean class="com.hspedu.spring.aop.homework.xml.MyCalAOP" id="myCalAOP" />

   <!--配置MyCal-bean-->
   <bean class="com.hspedu.spring.aop.homework.xml.MyCal" id="myCal"/>

   <!--配置切面类-->
   <aop:config>
      <!--配置切入点表达式-->
      <aop:pointcut id="myPointCut" expression="execution(public int com.hspedu.spring.aop.homework.xml.MyCal.*(int))"/>

      <!--配置前置,返回通知-->
      <aop:aspect ref="myCalAOP" order="10">
         <aop:before method="calStart" pointcut-ref="myPointCut"/>

         <aop:after-returning method="calEnd" pointcut-ref="myPointCut"/>
      </aop:aspect>
   </aop:config>
</beans>

 

运行结果:

 

标签:hspedu,java,spring,bean,AOP,import,com,public
From: https://www.cnblogs.com/romantichuaner/p/18456370

相关文章

  • springboot超市商品管理系统-计算机毕业设计源码55289
    摘 要随着信息技术的快速发展和普及,传统的超市管理模式已经无法满足现代商业的需求。为了提高超市的管理效率,优化商品销售流程,本文提出了一种基于SpringBoot框架的超市商品管理系统。该系统结合了现代软件开发技术,包括MySQL数据库、Java语言等,实现了对超市商品的全面管理。......
  • springboot社区团购系统-计算机毕业设计源码50782
    摘要随着互联网技术的不断发展和普及,社区团购作为一种新型的互联网零售模式,已经成为了人们越来越喜欢的购物方式之一。社区团购系统是支撑社区团购业务的重要基础设施,其功能和性能的好坏直接影响到社区团购业务的发展和用户体验。本论文研究了社区团购系统的需求分析、系统实......
  • springboot校园运动会管理系统-计算机毕业设计源码94492
    摘要校园运动会作为学校重要的体育活动之一,对于促进学生身心健康、增强团队合作意识具有重要意义。为了更好地组织和管理校园运动会,开发了基于SpringBoot的校园运动会管理系统。该系统旨在整合现代信息技术,提高运动会的组织效率和参与体验。通过该系统,学校可以方便地进行运......
  • 体检预约毕业设计社区体检管理网站预约排号基于SpringBootSSM框架
    目录1、项目概述‌1.1开发背景‌2、技术选择2.1IDEA开发工具2.2SpringBoot框架‌3需求分析3.1功能模块设计‌3.2非功能需求‌‌4、数据库设计‌‌5、界面设计‌‌6、安全性设计‌1、项目概述‌随着社区居民健康意识的不断提升,体检服务的需求也日益增长。然......
  • Spring Boot视频网站:用户体验与性能优化
    3系统分析3.1系统可行性分析需要使用大部分精力开发的视频网站系统为了充分降低开发风险,特意在开发之前进行可行性分析这个验证系统开发是否可行的步骤。本文就会从技术角度,经济角度,还有用户使用的程序的运行角度进行综合阐述。3.1.1技术可行性分析开发程序选择的是面......
  • springboot基于java的汽车票网上预订系统(源码+java+vue+部署文档+讲解等)
    收藏关注不迷路!!......
  • springboot基于java的社区健康管理系统(源码+java+vue+部署文档+讲解等)
    收藏关注不迷路!!......
  • springboot基于java的社区医院药品管理系统(源码+java+vue+部署文档+讲解等)
    收藏关注不迷路!!......
  • springboot接口,放回404 Bad Request
    分析:这种报错,通常都是json格式有误,导致的,比如说接口接受的对象是JSONArray,但是传进来的参数是JSONObject类型2024-10-1610:39:07.555WARN18536---[io-8688-exec-10].w.s.m.s.DefaultHandlerExceptionResolver:Resolved[org.springframework.http.converter.HttpMessag......
  • SpringBootAOP
    ​ 概念1.AspectOrientedProgramming面向切面编程处理面向对象编程中业务需求重复的部分,作为横切面插入到面向对象当中,一般有固定的应用场景,例如日志记录,登录校验,数据验证,统计耗时,事务管理等(AOP是OOP的延伸,简单一句话就是对基于面向对象编程的某些业务方法进行增强......