美文网首页
SpringAOP-4

SpringAOP-4

作者: 煗NUAN | 来源:发表于2020-03-01 21:54 被阅读0次

SpringAOP实现代理-6(注解,组件)

  • 注解的方式实现切面类,并且5种通知方法都可以使用注解完成
    • 环绕通知参数列表需传入 ProceedingJoinPoint , ProceedingJoinPoint执行proceed方法的作用是让目标方法执行,这也是环绕通知和前置、后置通知方法的一个最大区别.
  • aspectj-autoproxy标签实现自动代理
  • 需要在代理的beans.xml文件中添加context的声名
xmlns:context="http://www.springframework.org/schema/context"
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-aop.xsd">
  • 注解为了避免相同的匹配规则被定义多处,专门定义一个方法设置执行的匹配规则,需要调用相同规则的,直接调用该方法即可

  • 目标类UserServiceImpl.java(已实现接口)

public class UserServiceImpl implements UserService {

    @Override
    public List getUser() {
        System.out.println("getUser");
        return null;
    }

    @Override
    public boolean saveUser(Object user) {
        System.out.println("saveUser");
        return false;
    }

    @Override
    public boolean deleteUser(int userId) {
        System.out.println("deleteUser");
        return false;
    }

    @Override
    public boolean updataUser(Object user) {
        System.out.println("updataUser");
        return false;
    }
}
  • 以注解的方式实现切面类Aspect.java
  • 当前类的5种通知方法均以注解的方式完成
    • JoinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象.
      • getSignature();获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息
      • getArgs();获取传入目标方法的参数对象
      • getTarget();获取被代理的对象
      • getThis();获取代理对象
@Component    //标注当前类是一个组件
@org.aspectj.lang.annotation.Aspect   //标注当前类是一个切面类
public class Aspect {

    /**
    * Description: 定义一个注解方法,其中匹配规则是(* com.aop.aop6.*.*(..)),当其他通知匹配该规则的时候,直接在注解中引用该方法
    */
    @Pointcut(value ="execution(* com.aop.aop6.*.*(..))")
    public void proxyAll(){}

    /**
    * Description: 表示前置通知
    */
    @Before("proxyAll()")
    public void beforeTest(JoinPoint jp){
        //在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象.
        System.out.println("args "+jp.getArgs());
        System.out.println("before");
    }

    /**
    * Description: 表示后置通知
    */
    @After("proxyAll()") 
    public void afterTest(){
        System.out.println("after");
    }

    /**
    * Description: 环绕通知
    * @param: pjp:处理连接点
    */
    @Around("proxyAll()")
    public Object aroundTest(ProceedingJoinPoint pjp){  //环绕通知要有返回内容
        //环绕通知 ProceedingJoinPoint 执行proceed方法的作用是让目标方法执行,
        // 这也是环绕通知和前置、后置通知方法的一个最大区别。
        Object obj=null;
        try {

            System.out.println("around before");
            obj = pjp.proceed();  //环绕通知需要传入ProceedingJoinPoint并执行proceed方法.
            System.out.println("around after");

        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return obj;
    }

    /**
    * Description: 表示带返回值的通知
    * @param: jpj :连接点  ;   obj : 业务方法的返回值
    */
    @AfterReturning(value = "proxyAll()" , returning = "obj")
    public void returnTest(JoinPoint jp,Object obj){
        System.out.println("after return");
        System.out.println(obj);
        System.out.println("jp-toString "+jp.toString());
    }

    /**
    * Description: 带异常的通知
    * @param: jpj :连接点  ;   e : Throwable对象
    */
    public void throwingTest(JoinPoint jp,Throwable e){
        System.out.println("after throwing");
        System.out.println(e.getMessage());
    }
}
  • 代理的.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"
       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/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <!--
        context:component-scan :组件扫描
        base-package : 指定扫描包的路径
    -->
    <context:component-scan base-package="com.aop.aop6" />

    <!--aop:aspectj-autoproxy : 实现自动代理-->
    <aop:aspectj-autoproxy />
</beans>
  • 单元测试
public class AOPTest6 {
    @Test
    public void testAOP(){
        ApplicationContext ac=new ClassPathXmlApplicationContext("aop6/beans.xml");
        UserService userService =ac.getBean("us", UserService.class);

        userService.getUser();
        userService.saveUser(new Object());
        userService.deleteUser(2);
        userService.updataUser(new Object());
    }
}

SpringAOP-实现代理-7(BeanPostProcessor)

  • BeanPostProcessor接口是Bean后置处理器。接口实现类有postProcessBeforeInitialization和 afterProcessBeforeInitialization两个方法,分别在bean创建前和创建后出发调用. 因此bean创建之前或者之后对对应bean进行一些处理。这两个方法最终的返回值还是bean。可以是本身,也可以是加工过之后的
  • 利用beanpostProcessor,捕获部分bean的加载,并对这些bean生成动态代理,并将代理类放到spring容器中,后续调用原本这个bean类的方法时,就会直接进代理类的invoke方法,实现对某些类的某些方法(利用自定义注解)的增强等
  • 创建代理类:利用Proxy.newProxyInstance()方法创建真正的代理类。在postProcessAfterInitialization方法中。 返回的代理类接口的实现子类,不是原实现类的子类。这里是对接口进行代理。
  • 采用配置文件式,需要在代理的beans.xml文件中添加context的声名
xmlns:context="http://www.springframework.org/schema/context"
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-aop.xsd">
  • 目标类UserServiceImpl.java(已实现接口)
public class UserServiceImpl implements UserService {

    @Override
    public List getUser() {
        System.out.println("getUser");
        return null;
    }

    @Override
    public boolean saveUser(Object user) {
        System.out.println("saveUser");
        return false;
    }

    @Override
    public boolean deleteUser(int userId) {
        System.out.println("deleteUser");
        return false;
    }

    @Override
    public boolean updataUser(Object user) {
        System.out.println("updataUser");
        return false;
    }
}
  • 以注解的方式实现切面类Aspect.java
public class Aspect {

    public void before(){
        System.out.println(" Aspect before");
    }

    public void after(){
        System.out.println("Aspect after");
    }
}
  • 实现扩展接口BeanPostProcessor
/**
* Description: 配置了包扫描后,该类会初始化两个对象EventListenerMethodProcessor和DefaultEventListenerFactory,再外加我们自己的组件对象
    所以会有三个before打印
*/
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
        System.out.println("before");
        System.out.println("bean "+o);
        return o;
    }

    @Override
    public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
        System.out.println("after");
        return Proxy.newProxyInstance(MyBeanPostProcessor.class.getClassLoader(), o.getClass().getInterfaces(), new InvocationHandler() {
            //使用匿名回调函数InvocationHandler()
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                Aspect aspect=new Aspect();
                aspect.before();

                Object obj = method.invoke(o, args);

                aspect.after();
                return obj;
            }
        });
    }
}
  • 代理的.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <!--
        context:component-scan :上下文的组件扫描
        base-package : 指定扫描包的路径
    -->
    <context:component-scan base-package="com.aop.aop7" />

    <!--
       指定BeanPostProcessor的Factory hook(钩子),让每个bean(对应MyBeanPostProcessor中的参数o)对象初始化是自动回调该对象中的回调方法
    -->
    <bean class="com.aop.aop7.MyBeanPostProcessor" />
</beans>
  • 单元测试
public class AOPTest7 {
    @Test
    public void testAOP(){
        ApplicationContext ac=new ClassPathXmlApplicationContext("aop7/beans.xml");
        UserService userService =ac.getBean("us", UserService.class);

        userService.getUser();
        userService.saveUser(new Object());
        userService.deleteUser(2);
        userService.updataUser(new Object());
    }
}

相关文章

  • SpringAOP-4

    SpringAOP实现代理-6(注解,组件) 注解的方式实现切面类,并且5种通知方法都可以使用注解完成环绕通知参数...

网友评论

      本文标题:SpringAOP-4

      本文链接:https://www.haomeiwen.com/subject/eralkhtx.html