您好,欢迎来到知库网。
搜索
您的当前位置:首页Spring (2) AOP

Spring (2) AOP

来源:知库网

代理模式

为什么要学习代理模式?因为这就是SpringAOP的底层。

静态代理

角色分析: 

1.抽象角色:一般会使用接口或者抽象类来解决。

//租房
public interface Rent {
    public void rent();
}

2.真实角色:被代理的角色

public class Host implements Rent{

    @Override
    public void rent() {
        System.out.println("房东要出租房子......");
    }
}

3.代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作。

//如果继承房东的话,会有局限性,比如只能继承一个类,
// 比如中介不会只代理一个房东
public class Proxy implements Rent{
    Host host;

    public Proxy() {
    }

    public Proxy(Host host) {
        this.host = host;
    }

    @Override
    public void rent() {
        seeHouse();
        hetong();
        host.rent();
    }

    //看房
    public void seeHouse(){
        System.out.println("中介带你看房");
    }

    //签合同
    public void hetong(){
        System.out.println("和中介签合同");
    }
}

4.客户:访问代理角色

public class Client {

    public static void main(String[] args) {
        //房东要租房子
        Host host = new Host();
        //中介代理房东租房子,但是代理一般会有一些附属操作,例如签合同,收中介费
        Proxy proxy = new Proxy(host);
        //通过代理租房子,你不用面对房东,直接找中介租房即可。
        proxy.rent();
    }
}

静态代理模式的好处:1.可以使真实角色的操作更加纯粹,不用去关注一些公共的业务。2.公共的业务就交给代理角色!实现了业务的分工!3.公共业务发生扩展时,方便集中管理!

静态代理模式的缺点:一个真实角色就会产生一个代理对象,代码量会翻倍,开发效率会变低。

加深理解

1.抽象角色:一般会使用接口或者抽象类来解决。

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();

}

2.真实角色:被代理的角色

//真实对象
public class UserServiceImpl implements UserService{

    //想增加日志功能,不能在这个类中直接加方法,而是再创建一个类
    //原因:改动原有的业务代码,在公司中是大忌

    @Override
    public void add() {
        System.out.println("增加了一个用户。。。。。");
    }

    @Override
    public void delete() {
        System.out.println("删除了一个用户。。。。。");
    }

    @Override
    public void update() {
        System.out.println("更新了一个用户。。。。。");
    }

    @Override
    public void query() {
        System.out.println("查询了一个用户。。。。。");
    }
}

3.代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作。

//代理
public class UserServiceProxy implements UserService{
    UserService userService;

    //Spring不建议使用构造器注入,建议使用set方法注入
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    @Override
    public void add() {
        log("add");
        userService.add();
    }

    @Override
    public void delete() {
        log("delete");
        userService.delete();
    }

    @Override
    public void update() {
        log("update");
        userService.update();
    }

    @Override
    public void query() {
        log("query");
        userService.query();
    }

    //日志方法
    public void log(String method){
        System.out.println("使用了"+method+"方法。。。。");
    }
}

4.客户:访问代理角色

public class Client {

    public static void main(String[] args) {
        UserService userService=new UserServiceImpl();
        UserServiceProxy proxy = new UserServiceProxy();
        proxy.setUserService(userService);
        proxy.add();
    }
}

对应AOP:横切思想 

 动态代理

动态代理和静态代理的角色一样

动态代理的代理类是动态生成的,不是我们直接写好的。

动态代理分为两大类:基于类的的动态代理,基于类的动态代理。

基于接口--JDK动态代理【我们在这里使用】

基于类--cglib

Java字节码实现:javassist

需要了解两个类:Proxy:代理,InvocationHandler:调用处理程序

每一个代理实例都有一个关联的调用处理程序。InvocationHandler是由代理实例的调用处理程序 实现的接口(InvocationHandler是由代理实例来自动生成的一个调用处理程序的接口)。当在代理实例上调用方法时,方法调用将被编码并分配到其调用处理程序的invoke方法。

Proxy提供了创建动态代理类和实例的静态方法。

InvocationHandler提供invok方法,调用处理程序并返回一个结果

动态代理没有真实的代理角色。

1.抽象角色:一般会使用接口或者抽象类来解决。

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();
}

2.真实角色:被代理的角色

//真实对象
public class UserServiceImpl implements UserService{

    //想增加日志功能,不能在这个类中直接加方法,而是再创建一个类
    //原因:改动原有的业务代码,在公司中是大忌

    @Override
    public void add() {
        System.out.println("增加了一个用户。。。。。");
    }

    @Override
    public void delete() {
        System.out.println("删除了一个用户。。。。。");
    }

    @Override
    public void update() {
        System.out.println("更新了一个用户。。。。。");
    }

    @Override
    public void query() {
        System.out.println("查询了一个用户。。。。。");
    }
}

3.自动生成代理类:implements InvocationHandler。

//我们用这个类自动生成代理类
//这只是一个处理程序,而不是代理类
public class ProxyInvocationHandler implements InvocationHandler {

    //被代理的接口
    Object target;

    public void setTarget(Object target) {
        this.target=target;
    }

    //生成得到代理实例
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }

    //处理代理实例,并返回结果
    //当调用被代理的方法时,就会执行invoke方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log(method.getName());
        //动态代理的本质就是使用反射机制
        Object result = method.invoke(target, args);
        return result;
    }

    public void log(String msg){
        System.out.println("执行了"+msg+"方法。。。。");
    }
}

4.客户:访问代理角色

public class Client {

    public static void main(String[] args) {
        //真实角色
        UserService userService=new UserServiceImpl();
        //代理角色:现在没有
        ProxyInvocationHandler handler = new ProxyInvocationHandler();
        //通过调用程序处理角色来处理我们要调用的接口对象
        //动态代理 代理的是接口
        handler.setTarget(userService);
        //这里的proxy就是动态生成的,我们并没有写!
        UserService proxy = (UserService)handler.getProxy();
        proxy.add();
    }
}

动态代理模式的好处:1.可以使真实角色的操作更加纯粹,不用去关注一些公共的业务。2.公共的业务就交给代理角色!实现了业务的分工!3.公共业务发生扩展时,方便集中管理!4.一个动态代理类代理的是一个接口,一般就是对应的一类业务。5.一个动态代理类可以代理多个类,只要实现了同一个接口。

AOP

AOP(Aspect Oriented Programming):面向切面编程,通过预编译方法和运行期动态代理实现程序的统一维护的一种技术。

横切关注点:跨越应用程序多个模块的方法或功能。即,与我们的业务逻辑无关的,但是我们需要关注的部分,就是横切关注点,如日志,安全,缓存。

切面(Aspect):横切关注点 被模块化 的特殊对象。即,它就是一个类。

通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。

目标(Target):被通知的对象。

代理(Proxy):向目标对象应用通知之后创建的对象。

切入点(Pointcut)

连接点(JoinPoint):与切入点匹配的点,它和切入点差不多,但是可以从切入点中拿到一些东西。

AOP除了spring-mvc,还需要导入织入的jar包

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>

方式一:使用Spring的API接口【主要是SpringAPI接口实现】

配置ApplicationContext,加上标签和约束

        xmlns:aop="http://www.springframework.org/schema/aop"

        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd
public class AfterLog implements AfterReturningAdvice {
    //returnValue:返回值
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行了"+method.getName()+"方法,返回结果为"+returnValue);
    }
}
public class Log implements MethodBeforeAdvice {
    //method:要执行的目标对象的方法
    //objects:参数
    // o:目标对象
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println(o.getClass().getSimpleName()+"的"+method.getName()+"被执行了");
    }
}

在使用execution()表达式时,需要添加具体的切入点信息,例如:

1、execution():表达式主体。
 2、第一个*号:表示返回类型,*号表示方法返回的类型。
 3、包名:表示需要拦截的包名,后面的两个句点分别表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。
 4、第二个*号:表示类名,*号表示所有的类。
 5、*(..) :第三个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。

对于表达式:execution(* com.example.service..(..)),第一个*代表方法的返回类型,这里表示匹配任意返回类型的方法;com.example.service是指要匹配的方法所在的包名;..表示包及其子包下的所有方法;(..)表示匹配任意参数的方法

  • execution(* com.example.service..(..)):匹配com.example.service包下的任意方法。
  • execution(* com.example.service.UserService.*(..)):匹配com.example.service.UserService下的任意方法。
    <!--注册Beans    -->
    <bean id="userService" class="com.kuanghui.service.UserServiceImpl"/>
    <bean id="log" class="com.kuanghui.log.Log"/>
    <bean id="after" class="com.kuanghui.log.AfterLog"/>

    <!--第一种:使用原生的Spring API接口 -->
    <!--配置AOP,需要导入aop的约束   -->
    <aop:config>
        <!--切入点  UserServiceImpl.*表示UserServiceImpl下的所有方法  (..)表示可以有任意的参数-->
        <aop:pointcut id="pointcut" expression="execution(* com.kuanghui.service.UserServiceImpl.* (..))"></aop:pointcut>

        <!--执行环绕   -->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="after" pointcut-ref="pointcut"/>

    </aop:config>

 Test

    @Test
    public void test01(){
        ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        //动态代理代理的是接口,所有必须写getBean("userService", UserService.class)
        UserService userService = context.getBean("userService", UserService.class);
        userService.add();
    }

方式二:自定义实现AOP【主要是切面定义】

切面

public class DiyPointCut {

    public void before(){
        System.out.println("=====方法执行前=====");
    }

    public void after(){
        System.out.println("====方法执行后===");
    }

}
    <!--方式二-->
    <bean id="userService" class="com.kuanghui.service.UserServiceImpl"/>
    <bean id="diy" class="com.kuanghui.DIY.DiyPointCut"/>
    
    <aop:config>
        <!--自定义切面,ref要引用的类-->
        <aop:aspect ref="diy">
            <!-- 切入点-->
            <aop:pointcut id="pointcut" expression="execution(* com.kuanghui.service.UserServiceImpl.*(..))"/>
            <!--通知-->
            <aop:before method="before" pointcut-ref="pointcut"/>
            <aop:after method="after" pointcut-ref="pointcut"/>
        </aop:aspect>

    </aop:config>
    @Test
    public void test01(){
        ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        //动态代理代理的是接口,所有必须写getBean("userService", UserService.class)
        UserService userService = context.getBean("userService", UserService.class);
        userService.add();
    }

方式三:使用注解实现

要开启注解支持

    <!--开启注解支持!-->
    <aop:aspectj-autoproxy/>

 切面

@Aspect
public class AnnotationPoint {

    //@Before里面写切入点表达式
    @Before("execution(* com.kuanghui.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("====方式执行前====");
    }

    @After("execution(* com.kuanghui.service.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("====方式执行后====");
    }

    //在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入点
    @Around("execution(* com.kuanghui.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint point) throws Throwable {
        System.out.println("====方式环绕前====");
        //执行方法
        Object proceed = point.proceed();
        System.out.println("====方式环绕后====");
    }
}
    <!--方式三-->
    <bean id="annotationPointcut" class="com.kuanghui.DIY.AnnotationPoint"/>
    <bean id="userService" class="com.kuanghui.service.UserServiceImpl"/>
    <!--开启注解支持!-->
    <aop:aspectj-autoproxy/>

test

    @Test
    public void test01(){
        ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        //动态代理代理的是接口,所有必须写getBean("userService", UserService.class)
        UserService userService = context.getBean("userService", UserService.class);
        userService.add();
    }

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- zicool.com 版权所有 湘ICP备2023022495号-2

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务