spring 之AOP与其xml和注解方式(03)

版权声明:本文为博主原创文章,转载请注明出处 https://blog.csdn.net/yjy91913/article/details/76371682

aop:

概念:

预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续

作用:

利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

常用的场景:

   日志记录
   权限控制
   事务控制
   性能测试

底层使用的动态代理

常见的技术:

jdk:

前提必须有接口实现 Proxy.newProxyInstance(类加载器,需要实现的接口,执行处理类)
####spring中使用:
CGLIB:核心对象enhancer对象

hibernate和struts

用的是这个,javassist

下面我用Proxy和enhancer对象写两个例子:
需求:在dao对象 update方法前打印一个当前时间:

proxy代码

//被代理对象
        final OrderDao orderDao = new OrderDaoImpl();

        //创建代理对象
        OrderDao orderDaoProxy = (OrderDao)Proxy.newProxyInstance(orderDao.getClass().getClassLoader(), orderDao.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //获取update方法
                if("update".equals(method.getName())){
                    System.out.println(new Date().toLocaleString());
                }

                //执行原来的操作
                return method.invoke(orderDao, args);
            }
        });

下面是用CGLIB实现

//创建被代理对象
        final UserDao userDao = new UserDao();

        //创建代理对象
        UserDao userDaoProxy = (UserDao)Enhancer.create(userDao.getClass(), new MethodInterceptor() {

            @Override
            /**
             * 前三个参数和jdk中proxy中的InvocationHandler的参数一样
             * MethodProxy:方法的代理对象(我们在这用不到)
             */
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                if("find".equals(method.getName())){
                    System.out.println(new Date().toLocaleString());
                }
                //执行原来的逻辑
                return method.invoke(userDao, args);
            }
        });

aop中的术语

英文 名称 用法 备注
joinpoint 连接点 可以被增强的方法 例如:save update delete find
pointcut 切入点 被增强的方法 例如:update
advice 增强/通知 增强的代码
target 目标对象 被增强的对象 例如:userDao
weaving 织入 将通知添加到目标对象的切入点上过程
proxy 代理对象 增强后产生的对象
aspect 切面 切入点和通知的组合

基于aspectj的aop的xml方式入门

步骤:

1.导入jar包

核心的6个+aop的4个
aop4个是:

spring核心包:
   aop
   spring整合aspectj :spring-aspects-4.2.4.RELEASE.jar

spring依赖包:
   aop联盟(规范) :org.aopalliance
   aspectj :org.aspectj

2.创建配置文件

导入约束(beans和aop)

<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 http://www.springframework.org/schema/aop/spring-aop.xsd">

                </beans>

3.创建目标类

CustomerDao 和 CustomerDaoImpl

public class CustomerDaoImpl implements CustomerDao{
    public void save(){
        System.out.println("customerDao中的save执行了");
    }
    public void delete(){
        System.out.println("customerDao中的delete执行了");
    }
    public void update(){
        System.out.println("customerDao中的update执行了");
    }
    public void find(){
        System.out.println("customerDao中的find执行了");
    }
}

创建切面(通知/增强)类(放的是增强的代码)

public class MyAspectXml {

    /**
     * 要在customerdao中的delete方法执行之前调用
     */
    public void printTime(){
        System.out.println(new Date().toLocaleString());
    }
}

将目标类和切面类加入spring容器中

<bean id="customerDao" class="com.xx.c_aop_hello.CustomerDaoImpl"></bean>
    <bean id="myAspect" class="com.xx.c_aop_hello.MyAspectXml"></bean>

aop配置(增强方法)

格式:

<aop:config>
        //1定义切入点
        <aop:pointcut expression="execution(切入点表达式)" id="切入点名字"/>

        //2配置切面
        <aop:aspect ref="切面类">
            <aop:通知类型 method="切面类中的某个方法(通知)" pointcut-ref="切入点名字"/>
        </aop:aspect>
    </aop:config>

配置代码入下:

<!-- aop配置 -->
<aop:config>
    <!-- 1.定义切入点 -->
    <aop:pointcut expression="execution(* com.xx.c_aop_hello.CustomerDaoImpl.delete())" id="pointcut1"/>

    <!-- 2.配置切面 -->
    <aop:aspect ref="myAspect">
        <aop:before method="printTime" pointcut-ref="pointcut1"/>
    </aop:aspect>
</aop:config>

切入点表达式:

  必须使用execution函数引入 execution(表达式)
  表达式格式:
  [方法的修饰符] 返回值 包名.类名.方法名(参数列表)
  例如:

表达式 作用
com.xx.c_aop_hello.CustomerDaoImpl.delete() 指定包下的指定类的无参的delete
com.xx.c_aop_hello.CustomerDaoImpl.delete(..) 指定包下的指定类的任何参数的delete
com.xx.c_aop_hello.CustomerDaoImpl.*() 指定包下的指定类的无参的所有方法
com.xx.c_aop_hello..(..) 指定包下的所有类的所有方法
com.xx...(..) 指定包下及其子包下的的所有类的所有方法
com.xx...save(..) 指定包下及其子包下的的所有类的以save开头的方法
com.xx.service.CustomerDao+.*(..) 指定包下的指定类及其子类和实现类的所有方法

通知类型:

通知类型 何时执行 code
前置通知 在切入点执行前执行的方法 before
后置通知 在切入点执行后执行的方法,还可以获取切入点的返回值 after-returning
环绕通知 在切入点执行前和后要执行的方法 around
异常通知 在切入点执行前和后要执行的方法 around
环绕通知 在切入点执行时抛异常的时候要执行的方法 还可以获取异常信息 after-throwing
最终通知 在切入点执行后都要执行的方法 after

异常通知,后置通知,环绕通知详解:

 如果想获取after-returning绑定目标方法的返回值,就加上returning标签

<aop:after-returning method="afterReturningMethod" pointcut-ref="pt2" returning="res"/>

 然后在通知就可以加入这个变量了

public void afterReturningMethod(Object res){
        System.out.println("------afterReturning------:"+res);
    }

 这样就能把这个的目标对象的返回值打印出来了

 同理,如果想获取AfterThrowing绑定的目标方法的返回值,就加上throwing属性

 这个环绕通知原理跟动态代理差不多,配置没什么特别的,但是通知有点不太一样,写法是这样的:

public Object aroundMethod(ProceedingJoinPoint jp) throws Throwable{
        System.out.println("------around:1------");
        //让切入点执行
        Object res = jp.proceed();
        System.out.println("------around:2------");
        return res;
    }
<aop:after-throwing method="afterThrowingMethod" pointcut-ref="pt4" throwing="ex"/>

 打印异常信息

public void afterThrowingMethod(Throwable ex){
        System.out.println("------afterThrowing------"+ex.getMessage());
    }

案例:aop的注解方式

1.xml和注解混合方式

步骤:

1.导入jar:11个

jar包

2.创建配置文件,导入beans aop context约束

<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"
    xmlns:tx="http://www.springframework.org/schema/tx"
    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
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/tx 
    http://www.springframework.org/schema/tx/spring-tx.xsd">

3.编写目标类

@Repository("linkmanDao")
public class LinkmanDao {

    public void save(){
        System.out.println("linkmanDao中的save执行了");
    }
    public int delete(){
        System.out.println("linkmanDao中的delete执行了");
        return 100;
    }
    public void update(){
        System.out.println("linkmanDao中的update执行了");
    }
    public void find(){
        System.out.println("linkmanDao中的find执行了");
        //int i = 1/0;
    }

4.编写切面类

@Component("myAspectAdvice")
@Aspect//声明它是一个切面类
public class MyAspectAdvice {

    //@Before(value="execution(* com.xx.d_aop_advice.LinkmanDao.save(..))")
    @Before("execution(* com.xx.a_aop_hello.LinkmanDao.save(..))")
    public void beforeMethod(){
        System.out.println("------before------");
    }

    @AfterReturning(value="execution(* com.xx.a_aop_hello.LinkmanDao.delete(..))",returning="res")
    public void afterReturningMethod(Object res){
        System.out.println("------afterReturning------:"+res);
    }

    @AfterThrowing(value="execution(* com.xx.a_aop_hello.LinkmanDao.find(..))",throwing="ex")
    public void afterThrowingMethod(Throwable ex){
        System.out.println("------afterThrowing------"+ex.getMessage());
    }

    @Around(value="execution(* com.xx.a_aop_hello.LinkmanDao.update(..))")
    public Object aroundMethod(ProceedingJoinPoint jp) throws Throwable{
        System.out.println("------around:1------");
        //让切入点执行
        Object res = jp.proceed();
        System.out.println("------around:2------");
        return res;
    }
    @After(value="execution(* com.xx.a_aop_hello.LinkmanDao.find(..))")
    public void afterMethod(){
        System.out.println("------after------");
    }

注:下面几步上面代码已经编好

5.给目标类和切面类添加注解 加入spring容器

6.在切面类上添加注解 @Aspect //声明切面类

7.在切面类的方法上添加通知注解

@Before(value=”execution(表达式)”)

8.在配置文件中开启组件扫描\在配置文件中开启aop注解自动代理

    <!-- 开启组件扫描 -->
    <context:component-scan base-package="com.xx"/>
    <!-- 在配置文件中开启aop注解自动代理-->
    <aop:aspectj-autoproxy/>

注解小结

切面类上的注解

@Aspect

切面类中的方法上的注解(通知注解)

@Before(value=”execution(表达式)”)
@AfterReturning(value=”execution(表达式)”)
@Around(value=”execution(表达式)”)
@AfterThrowing(value=”execution(表达式)”)
@After(value=”execution(表达式)”)

切入点注解的切入点注解方式

在切面类中提供给一个方法(方法名就相当于之前的id值)
在方法上添加注解
@Pointcut(value=”execution(表达式)”)

通知中就可以通过以下注释添加

@Before(value=”类名.切入点方法名()”)
例如:


//@After(value="execution(* com.xx.a_aop_hello.*.find(..))")   --这个是之前的方式
    @After(value="MyAspectAdvice.pt1()")
    public void afterMethod(){
        System.out.println("------after------");
    }

    @Pointcut(value="execution(* com.xx.a_aop_hello.*.find(..))")
    public void pt1(){}

纯注解方式(了解)

创建一个配置类:

能做以下两件事
开启组件扫描
开启aop注解的自动代理
就可以删除配置文件了,其他的与上述的混合方式一样

在配置类上添加注解:

@ComponentScan(value=”包名”)
@EnableAspectJAutoProxy

代码:

@ComponentScan(value="com.xx")
@Configuration
public class SpringConfig {

}

猜你喜欢

转载自blog.csdn.net/yjy91913/article/details/76371682