Spring系列(三)AOP详解

一.Spring—AOP概述

AOP:面向切面编程
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

二.AOP

直接上代码,具体解释见代码注释
这里写图片描述
AOP.java

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:/cjx/aop/aop.xml")
public class AOP {//AOP测试
    //Interface接口相当于UserService
    //Target类相当于UserServiceImpl
    @Resource(name="target")
    private Interface us;

    @Test
    public void fun(){
        us.delete();//此方法无异常
        System.out.println("——分割线——");
        us.save();//此方法内置了一个异常,用于演示
    }

    /*AOP:面向切面编程思想
     * 思想总结:横向重复的代码,用纵向AOP抽取出来。
     */

    /*动态代理可以体现AOP思想
     * 动态代理:对目标对象的方法进行增强
     * Cglib代理可以体现AOP思想
     * Cglib代理:对目标对象的方法进行增强
     */

    /*spring的AOP开发
     * spring封装了动态代理(基于接口)的代码和Cglib代理(基于继承)的代码
     * 使用 spring就不用手写动态代理的代码和Cglib代理的代码
     * 因此使用 spring就可以对任何类进行增强
     * 例如:/spring/src/cjx/aopCglib/AopCglib.java
     * 例如:/spring/src/cjx/aopProxy/AopProxy.java
     */

    /*spring的AOP名词解释
     * Joinpoint(连接点):目标对象中,所有可以增强的方法
     * Pointcut(切入点):目标对象中,所有已经增强(想要增强)的方法
     * Advice(通知/增强):增强目标对象的代码
     * Target(目标对象):被代理的对象
     * Weaving(织入):将通知应用到切入点的过程
     * Proxy(代理):将通知织入到目标对象之后,形成代理对象
     * Aspect(切面):切入点+通知
     */
}

aop.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans" 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-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">
    <!-- 使用AOP配置 在/spring/src/cjx/beanAnnotate/beanAnnotate.xml基础上 导入约束 
        ···\spring-framework-4.2.4.RELEASE\schema\aop\spring-aop-4.2.xsd -->
    <!-- 导包 ···\spring-framework-4.2.4.RELEASE\_spring-framework-3.0.2.RELEASE-dependencies\org.aopalliance\com.springsource.org.aopalliance\1.0.0\com.springsource.org.aopalliance-1.0.0.jar 
        ···\spring-framework-4.2.4.RELEASE\_spring-framework-3.0.2.RELEASE-dependencies\org.aspectj\com.springsource.org.aspectj.weaver\1.6.8.RELEASE\com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar 
        spring-aspects-4.2.4.RELEASE.jar -->
    <!--配置AOP -->
    <!--1.配置目标对象 -->
    <bean name="target" class="cjx.aop.Target"></bean>
    <!--2.配置通知对象 -->
    <bean name="advice" class="cjx.aop.Advice"></bean>
    <!--3.配置将通知织入目标对象 -->
    <aop:config>
        <!--配置切入点:使用切入点表达式 execution() 
            public void cjx.aop.Target.save()——原始表达式 
            void cjx.aop.Target.save()——public 可省(默认值) 
            * cjx.aop.Target.save()——不对返回值做要求 
            * cjx.aop.Target.*()——cjx.aop.Target包下的所有空参方法 
            * cjx.aop.Target.*(..)——不对参数做要求 
            * cjx.aop.*get.*(..)——cjx.aop包下以get结尾的类(不包括子包) 
            * cjx.aop..*get.*(..)——cjx.aop包下以get结尾的类(包括子包) -->
    <aop:pointcut expression="execution(* cjx.aop.Target.*(..))"  id="pc" />
    <aop:aspect ref="advice">
        <!--指定前置通知,并切入到切入点 -->
        <aop:before method="before" pointcut-ref="pc" />
        <!--指定后置通知,并切入到切入点 -->
        <aop:after-returning method="afterReturning" pointcut-ref="pc" />
        <!--指定环绕通知,并切入到切入点 -->
        <aop:around method="around" pointcut-ref="pc" />
        <!--指定异常拦截通知,并切入到切入点 -->
        <aop:after-throwing method="afterException" pointcut-ref="pc" />
        <!--指定后置通知,并切入到切入点 -->
        <aop:after method="after" pointcut-ref="pc" />

    </aop:aspect>
    </aop:config>
</beans>

Interface.java

public interface Interface {
    void save();
    void delete();
    void update();
    void find();
}

Advice.java

public class Advice {//通知类
    //Advice(通知/增强):增强目标对象的代码
    //定义通知时,方法名随意
    //前置通知:目标方法运行之前调用
    public void before(){
        System.out.println("前置通知");
    }
    //后置通知:目标方法运行之后调用,如果出现异常不会调用
    public void afterReturning(){
        System.out.println("后置通知,出现异常不会调用");
    }
    //环绕通知:目标方法运行之前和运行之后都调用
    //环绕通知必须接受一个ProceedingJoinPoint 对象
    //环绕通知必须手动调用目标方法,并返回目标方法
    public Object around(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("环绕通知之前部分");
        Object proceed = pjp.proceed();//调用目标方法
        System.out.println("环绕通知之后部分");
        return proceed;
    }
    //异常拦截通知:如果出现异常,就会调用
    public void afterException(){
        System.out.println("出现异常了");
    }
    //后置通知:目标方法运行之后调用,无论是否出现异常都会调用
    public void after(){
        System.out.println("后置通知,无论是否出现异常都会调用");
    }
}

Target.java

public class Target implements Interface{//目标对象类
    //Target(目标对象):被代理的对象
    @Override
    public void save() {
        System.out.println("save");     
        //用于演示异常
        int i=1/0;
        System.out.println(i);
    }
    @Override
    public void delete() {
        System.out.println("delete");       
    }
    @Override
    public void update() {
        System.out.println("update");       
    }
    @Override
    public void find() {
        System.out.println("find");     
    }
}

二.AOP注解

直接上代码,具体解释见代码注释
这里写图片描述
AdviceAnnotate.java


@Aspect//表示该类是通知类
public class AdviceAnnotate {//通知注解类    
    //抽取表达式的方法
    @Pointcut("execution(* cjx.aop.Target.*(..))")
    public void pc(){}  
    //指定该方法是前置通知,并指定切入点
    @Before("AdviceAnnotate.pc()")
    public void before(){
        System.out.println("前置通知AOPAnnotate");
    }   
    //指定该方法是后置通知,并指定切入点
    @AfterReturning("AdviceAnnotate.pc()")
    public void afterReturning(){
        System.out.println("后置通知,出现异常不会调用AOPAnnotate");
    }   
    //指定该方法是环绕通知,并指定切入点
    @Around("AdviceAnnotate.pc()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("环绕通知之前部分AOPAnnotate");
        Object proceed = pjp.proceed();//调用目标方法
        System.out.println("环绕通知之后部分AOPAnnotate");
        return proceed;
    }   
    //指定该方法是异常拦截通知,并指定切入点
    @AfterThrowing("execution(* cjx.aop.Target.*(..))")
    public void afterException(){
        System.out.println("出现异常了AOPAnnotate");
    }   
    //指定该方法是后置通知,并指定切入点
    @After("execution(* cjx.aop.Target.*(..))")
    public void after(){
        System.out.println("后置通知,无论是否出现异常都会调用AOPAnnotate");
    }
    //对比不使用注解的方式,发现输出顺序有区别,原因未知 
}

aopAnnotate.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans" 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-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">

    <!--使用注解配置AOP -->
    <!--1.配置目标对象(可以使用注解配置) -->
    <bean name="target" class="cjx.aop.Target"></bean>
    <!--2.配置通知对象(可以使用注解配置) -->
    <bean name="advice" class="cjx.aopAnnotate.AdviceAnnotate"></bean>
    <!--3.开启使用注解完成织入 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

AOPAnnotate.java

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:cjx/aopAnnotate/aopAnnotate.xml")
public class AOPAnnotate {
    @Resource(name="target")
    private Interface us;   
    @Test
    public void fun(){
        us.delete();//此方法无异常
        System.out.println("——分割线——");
        us.save();//此方法内置了一个异常,用于演示
    }
}

二.AOP原理

直接上代码,具体解释见代码注释

1.Cglib

这里写图片描述
AopCglib.java

扫描二维码关注公众号,回复: 2201947 查看本文章
//此包代码演示了spring的AopCglib代理部分原理,观光代码
public class AopCglib {//AopCglib代理,基于继承代理
//第三方代理技术,cglib代理.可以对任何类生成代理.代理的原理是对目标对象进行继承代理.
//如果目标对象被final修饰.那么该类无法被cglib代理.
    @Test
    public void fun(){
        UserServiceProxyFactory factory=new UserServiceProxyFactory();
        UserService usProxy=factory.getUserServiceProxy();
        usProxy.save();
        //判断代理对象是否属于被代理对象类型
        //代理对象继承了被代理对象,所以结果是true
        System.out.println(usProxy instanceof UserServiceImpl);
    }
    /*spring的AOP名词解释
     * Joinpoint(连接点):目标对象中,所有可以增强的方法
     * Pointcut(切入点):目标对象中,所有已经增强的方法
     * Advice(通知/增强):增强目标对象的代码
     * Target(目标对象):被代理的对象
     * Weaving(织入):将通知应用到切入点的过程
     * Proxy(代理):将通知织入到目标对象之后,形成代理对象
     * Aspect(切面):切入点+通知
     */
}

UserService.java

public interface UserService {
    void save();
    void delete();
    void update();
    void find();
}

UserServiceImpl.java

public class UserServiceImpl implements UserService{
    @Override
    public void save() {
        System.out.println("save");     
    }
    @Override
    public void delete() {
        System.out.println("delete");

    }
    @Override
    public void update() {
        System.out.println("update");       
    }
    @Override
    public void find() {
        System.out.println("find");     
    }
}

UserServiceProxyFactory.java

public class UserServiceProxyFactory implements MethodInterceptor{  
    public UserService getUserServiceProxy(){
        //生成代理对象
        Enhancer en=new Enhancer();
        //设置对谁进行代理
        en.setSuperclass(UserServiceImpl.class);
        //设置代理要做什么
        en.setCallback(this);
        //创建代理对象
        UserService us = (UserService) en.create();
        return us;
    }
    @SuppressWarnings("unused")
    @Override
    public Object intercept(Object proxyObj, Method method, Object[] arg, MethodProxy methodProxy) throws Throwable {
        //打开事务
        System.out.println("AopCglib打开事务");
        //调用原有方法
        Object returnValue = methodProxy.invokeSuper(proxyObj, arg);
        //提交事务
        System.out.println("AopCglib提交事务");
        return null;
    }
}

2.Proxy

这里写图片描述
AopProxy.java

//此包代码演示了spring的AopProxy代理部分原理,观光代码
public class AopProxy {//AOP代理,类似于动态代理(了解)
    //被代理对象必须要实现接口,才能产生代理对象.如果没有接口将不能使用动态代理技术
    @Test
    public void fun(){
        UserService us=new UserServiceImpl();
        UserServiceProxyFactory factory =new UserServiceProxyFactory(us);
        UserService usProxy=factory.getUserServiceProxy();
        usProxy.save();
        //代理对象与被代理对象实现了相同的接口
        //代理对象与被代理对象没有继承关系
        System.out.println(usProxy instanceof UserServiceImpl);
    }
    /*spring的AOP名词解释
     * Joinpoint(连接点):目标对象中,所有可以增强的方法
     * Pointcut(切入点):目标对象中,所有已经增强的方法
     * Advice(通知/增强):增强目标对象的代码
     * Target(目标对象):被代理的对象
     * Weaving(织入):将通知应用到切入点的过程
     * Proxy(代理):将通知织入到目标对象之后,形成代理对象
     * Aspect(切面):切入点+通知
     */
}

UserService.java

public interface UserService {
    void save();
    void delete();
    void update();
    void find();
}

UserServiceImpl.java

public class UserServiceImpl implements UserService{
    @Override
    public void save() {
        System.out.println("save");     
    }
    @Override
    public void delete() {
        System.out.println("delete");       
    }
    @Override
    public void update() {
        System.out.println("update");       
    }
    @Override
    public void find() {
        System.out.println("find");     
    }

}

UserServiceProxyFactory.java

public class UserServiceProxyFactory implements InvocationHandler{
    private UserService us; 
    public UserServiceProxyFactory(UserService us) {
        super();
        this.us = us;
    }
    public UserService getUserServiceProxy(){
        //生成动态代理
        UserService usProxy = (UserService) Proxy.newProxyInstance(UserServiceProxyFactory.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), this);
        //返回
        return usProxy;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("AopProxy打开事务!");
        Object invoke = method.invoke(us, args);
        System.out.println("AopProxy提交事务!");
        return invoke;
    }
}

猜你喜欢

转载自blog.csdn.net/bestmy/article/details/81069701
今日推荐