zoukankan      html  css  js  c++  java
  • Spring的aop思想(名词、aop的配置)

    1、AOP思想

    面向切面编程,采取横向抽取的方式,取代了纵向继承体系代码的重复性。底层采用代理的机制进行实现,是面向对象编程的延续,使得业务的耦合性降低,提高了程序的可重用性

    应用:

    事务管理、性能监控、安全检查、缓存、日志等

    (1)在解决中文乱码的应用:

     一般情况下如果在Servlet中要解决中文乱码问题需要在每一个Servlet中书写解决乱码的代码,但是,在运用了过滤器之后,就不再需要每一个Servlet中都写解决乱码的函数,减少了代码量。

    AOP思想又叫做“面向切面编程”,过滤器就是面向每一个Servlet的,每一个Servlet都需要执行过滤器。

    (2)动态代理

       代理类的那部分代码被固定下来了,不会因为业务的增加而逐渐庞大。动态代理的jdk实现方式在添加功能的时候,只需要在目标类和接口中添加功能即可,代理类的代码是固定的,不是像静态代理那样添加功能后既要在目标类中添加代码又要在接口和代理类中添加代码

    (3)拦截器

     不需要在每一个Action中书写拦截器的代码,只需要在Action需要的时候在配置文件中将相应的拦截器配置进去即可。

    2、Spring中的AOP

     Spring能够为容器中管理的对象生成动态代理对象。这样做是有好处的,如果把登录功能写在每一个业务中当需要修改代码的时候,就需要对每一个业务进行修改,但是,如果把登录功能抽取出来形成独立的模块,问题就迎刃而解了。

    3、Spring实现aop的原理

    (1)动态代理(优先使用):被代理对象必须要实现接口,才能产生代理对象,如果没有接口将不能实现动态代理。

     假如登录需要分为登录前、登录、登录后三部分,也就是说登录部分是相同的代码,而登录前和登录后的代码根据用户的不同进行不同的操作,那么,登录接口可以这样写:

    public class LoginService {
        public void userLogin(){
          System.out.println("登录前的操作");
          System.out.println("登录");
          System.out.println("登录后的操作");
        }
        public void managerLogin(){
            System.out.println("登录前的操作");
            System.out.println("登录");
            System.out.println("登录后的操作");
        }
    }

    这样写的话不仅会造成代码的冗余,而且,如果要修改登录的代码的话两个都要分别作出修改,可以使用动态代理的方式将公共部分抽取出来:

    定义登录接口:

    public interface LoginService {
        public void userLogin();
        public void managerLogin();
    }

    定义接口的实现类:

    public class LoginProxyImp implements LoginService{
        public void userLogin(){
            System.out.println("普通用户登录前的操作");
    
        }
        public void managerLogin(){
            System.out.println("管理员登录前的操作");
    
        }
    }

    创建代理类:

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    public class LoginProxy implements InvocationHandler {
        private  LoginService ls;
        public LoginProxy(LoginService ls) {
            super();
            this.ls = ls;
        }
        public LoginService  getloginProxy(){
            LoginService loginService= (LoginService) Proxy.newProxyInstance(LoginService.class.getClassLoader(),//创建接口实例
                    LoginProxyImp.class.getInterfaces(),//与目标对象有相同的类加载器
                     this);   //被代理的类所实现的接口(可以是多个)
            return loginService;//产生代理对象
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] arg2) throws Throwable {
            Object invoke=method.invoke(ls,arg2);
            System.out.println("登录");//抽取出来的公共部分
            return invoke;
        }
    }

    测试类:

    public class TestProxy {
        public static void main(String [] args){
            LoginService loginService=new LoginProxyImp();
            LoginProxy loginProxy=new LoginProxy(loginService);
            LoginService loginService1=loginProxy.getloginProxy();
            loginService1.userLogin();
        }
    }

    (2)cglib技术:可以对任何类生成代理对象,原理是对目标对象进行继承代理,如果该目标对象被final修饰,将无法使用cglib技术

    登录接口:

    public interface LoginService {
        public void userLogin();
        public void managerLogin();
    }

    接口的实现类:

    public class LoginProxyImp implements LoginService{
        public void userLogin(){
            System.out.println("普通用户登录前的操作");
    
        }
        public void managerLogin(){
            System.out.println("管理员登录前的操作");
    
        }
    }

    创建代理类:

    public class LoginProxy implements MethodInterceptor {
    
        public LoginService getLoginServiceProxy(){
            Enhancer enhancer=new Enhancer();//帮我们生成代理对象
            enhancer.setSuperclass(LoginProxyImp.class);//设置对谁进行代理,即确定父类
            enhancer.setCallback(this);//代理要做什么,即设定回调函数
            LoginService loginService=(LoginService) enhancer.create();//创建代理对象
            return loginService;
        }
    
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    //前三个参数和jdk方式的代理相同 Object invokeSuper = methodProxy.invokeSuper(o,objects); return invokeSuper; } }

    测试类:

    public class TestProxy {
        public static void main(String [] args){
            LoginProxy loginProxy=new LoginProxy();
            LoginService loginService=loginProxy.getLoginServiceProxy();
            loginService.userLogin();
            System.out.println("登录");
        }
    }

     这种方式是代理对象继承了被代理对象。

    4、aop中的名词

    (1)连接点(Joinpoint):目标对象中可以增强的的方法

    public class LoginProxyImp implements LoginService{
        public void userLogin(){
            System.out.println("普通用户登录前的操作");
    
        }
        public void managerLogin(){
            System.out.println("管理员登录前的操作");
    
        }
    }

    该方法还可以通过动态代理增加登录的功能。

    (2)切入点(PointCut):目标对象已经增强的方法

        public void userLogin(){
            System.out.println("普通用户登录前的操作");
    
        }
        public void managerLogin(){
            System.out.println("管理员登录前的操作");
    
        }

    例如:上述两个登录的方法通过动态代理已经增强的方法。

    (3)advice(通知 / 增强):Spring中一共有五种通知类型

      public Object invoke(Object proxy, Method method, Object[] arg2) throws Throwable {
            Object invoke=method.invoke(ls,arg2);
            System.out.println("登录");//抽取出来的公共部分
            return invoke;
        }

    加入了登录的功能,这部分代码是advice。

    (4)Target(目标对象):

    被代理的对象。

    (5)weaving(织入):

    将通知应用到切入点的形成代理类的过程。

    (6)proxy(代理):

    将通知织入到目标对象以后,形成代理对象

    (7)切面(aspect):切入点加通知

    5、Spring中的aop演示

    (1)导包:

     (2)对配置文件进行配置:

    需要先导入aop约束:

     (3)创建接口:

    public interface LoginService {
        public void userLogin();
        public void managerLogin();
    }

    (4)创建目标对象:

    public class LoginServiceImp {
        public void userLogin(){
            System.out.println("普通用户登录前的操作");
    
        }
        public void managerLogin(){
            System.out.println("管理员登录前的操作");
    
        }
    }

    (5)创建通知类

    (6)配置Spring的配置文件:

    <?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:util="http://www.springframework.org/schema/util"
           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/util http://www.springframework.org/schema/util/spring-util.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">
        <bean name="loginserviceTarget" class="pers.zhb.test.LoginServiceImp"></bean><!--配置目标对象-->
        <bean name="myadvice" class="pers.zhb.test.MyAdvice"></bean><!--配置通知对象-->
        <aop:config>
            <aop:pointcut  expression="execution(* pers.zhb.test.LoginServiceImp.userLogin())" id="pointcut"/>
            <aop:aspect ref="myadvice">
                <aop:before method="before" pointcut-ref="pointcut"/>
                <aop:after method="after" pointcut-ref="pointcut"/>
                <aop:after-throwing method="afterException" pointcut-ref="pointcut"/>
                <aop:around method="around" pointcut-ref="pointcut"/>
            </aop:aspect>
        </aop:config>
    
    </beans>

    (1)配置目标对象,即被代理的对象。

    (2)配置通知对象,即需要加入的那部分代码。

    (3)将通知织入目标对象:需要先配置接入点,即:userLogin(),将通知对象加入目标对象。

    好处:通过配置文件的形式实现aop的开发,我们就不需要手动书写动态代理的代码了,可以进行任何类的代理,不需要接口也可以。

    6、使用注解完成Spring的aop配置

    配置文件的书写:

    <?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:util="http://www.springframework.org/schema/util"
           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/util http://www.springframework.org/schema/util/spring-util.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">
        <bean name="loginserviceTarget" class="pers.zhb.test.LoginServiceImp"></bean><!--配置目标对象-->
        <bean name="myadvice" class="pers.zhb.test.MyAdvice"></bean><!--配置通知对象-->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy><!--开启使用注解完成织入-->
    </beans>

    使用注解配置:

    package pers.zhb.test;
    //通知类,即用于增强目标对象的代码
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    
    @Aspect//表示该类是一个通知类
    public class MyAdvice {
        @Before("execution(* pers.zhb.test.LoginServiceImp.userLogin())")//指定切入点(前置通知)
        public void before(){
           System.out.println("我是前置通知!");
        }
        @Around("execution(* pers.zhb.test.LoginServiceImp.userLogin())")
        public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            System.out.println("这是环绕通知之前的部分!");
            Object process=proceedingJoinPoint.proceed();//环绕通知
            System.out.println("这是环绕通知之后的部分!");
            return process;
        }
        @AfterThrowing("execution(* pers.zhb.test.LoginServiceImp.userLogin())")
        public void afterException(){
            System.out.println("异常出现了!");
        }
        @After("execution(* pers.zhb.test.LoginServiceImp.userLogin())")
        public void after(){
            System.out.println("后置通知,出现异常也能被调用!");
        }
        @AfterReturning("execution(* pers.zhb.test.LoginServiceImp.userLogin())")
        public void AfterReturning(){
            System.out.println("我是后置通知,出现异常不会执行!!");
        }
    }

     但是,用上面的方法配置的注解不利于修改,可以进行下面的配置:

    package pers.zhb.test;
    //通知类,即用于增强目标对象的代码
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    
    @Aspect//表示该类是一个通知类
    public class MyAdvice {
        @Pointcut("execution(* pers.zhb.test.LoginServiceImp.userLogin())")
        public void pc(){
    
        }
        @Before("MyAdvice.pc()")//指定切入点(前置通知)
        public void before(){
           System.out.println("我是前置通知!");
        }
        @Around("MyAdvice.pc()")
        public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            System.out.println("这是环绕通知之前的部分!");
            Object process=proceedingJoinPoint.proceed();//环绕通知
            System.out.println("这是环绕通知之后的部分!");
            return process;
        }
        @AfterThrowing("MyAdvice.pc()")
        public void afterException(){
            System.out.println("异常出现了!");
        }
        @After("MyAdvice.pc()")
        public void after(){
            System.out.println("后置通知,出现异常也能被调用!");
        }
        @AfterReturning("execution(* pers.zhb.test.LoginServiceImp.userLogin())")
        public void AfterReturning(){
            System.out.println("我是后置通知,出现异常不会执行!!");
        }
    }
  • 相关阅读:
    var在PHP和JS中的使用
    修改PHP上传文件大小限制的方法
    Linux中tail指令详解
    drupal7 profile2模块获取个人信息
    drupal7 STMP邮件模块配置
    drupal读取mysql的longblob字段
    drupal7 自定义登录&找回密码页面,注意事项
    转 VS Code 快捷键大全,没有更全
    权力关进笼子里
    drupal的权限设置
  • 原文地址:https://www.cnblogs.com/zhai1997/p/12300652.html
Copyright © 2011-2022 走看看