Java-动态代理(jdk,cglib)-SpringAop(面向切面编程)

一、为什么要使用动态代理

代理类在程序运行时创建的代理方式被成为动态代理。在静态代理中,代理类(RenterProxy)是自己已经定义好了的,在程序运行之前就已经编译完成。而动态代理是在运行时根据我们在Java代码中的“指示”动态生成的。动态代理相较于静态代理的优势在于可以很方便的对代理类的所有函数进行统一管理,如果我们想在每个代理方法前都加一个方法,如果代理方法很多,我们需要在每个代理方法都要写一遍,很麻烦。而动态代理则不需要。

二、如何解决当需要改变代码时,需要将新加的内容都全部写一遍?

(1)抽取个方法。缺点还需要再1w方法的地方调用。
(2)动态代理: 实现方式有两种: [1]JDK原生动态代理----缺点:必须基于接口完成 [2]cglib动态代理
(3)AOP面向切面编程:AOP的底层实现就是基于动态代理。

三、JDK原生动态代理

首先我们我们先以一个简单的数学计算方法模拟一下编程过程中的其他方法

1.创建一个MathService接口

package com.gsh.jdk;

public interface MathService {
    //加法
    double add(double a,double b);
    //减法
    double sub(double a,double b);
    //乘法
    double mul(double a,double b);
    //除法
    double div(double a,double b);
}

2.创建一个MathService接口的实现类

package com.gsh.jdk;

/**
 * @Auther: haohao
 * @Date:2022/7/708:53
 */
public class MathServiceImpl implements MathService{
    @Override
    public double add(double a, double b) {
        double result=a+b;
        return result;
    }

    @Override
    public double sub(double a, double b) {
        double result=a-b;
        return result;
    }

    @Override
    public double mul(double a, double b) {
        double result=a*b;
        return result;
    }

    @Override
    public double div(double a, double b) {
        double result=a/b;
        return result;
    }
}

3.创建一个代理类工厂 ProxyFactory

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyFactory {
    //被代理对象
     private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    //获取代理对象
    public Object getProxy(){
        /**
         * ClassLoader loader, 被代理对象的类加载器
         * Class<?>[] interfaces, 被代理对象实现的接口
         * InvocationHandler h: 当代理对象执行被代理的方法时,会触发该对象中的invoke功能
         */
        ClassLoader loader=target.getClass().getClassLoader();
        Class<?> [] interfaces=target.getClass().getInterfaces();

        InvocationHandler h=new InvocationHandler() {
            //Method:方法类---被代理的方法
            //args:参数
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("=======在方法运行之前我被执行了=======");
                Object result= method.invoke(target,args);//代理对象回调该方法
                System.out.println("*******在方法运行之后我被运行了*******");
                return result;
            }
        };
        Object o = Proxy.newProxyInstance(loader, interfaces,h);
        return o;
    }
}

4.测试

public class Test {
    public static void main(String[] args) {
           MathService mathService=new MathServiceImpl();
           ProxyFactory proxyFactory=new ProxyFactory(mathService);
           MathService proxy = (MathService) proxyFactory.getProxy();
           Object r=proxy.add(10,4);
           System.out.println(r);
    }
}

5.结果

 四、cglib动态代理

动态代理需要被代理类实现接口,如果被代理类没有实现接口,那么这么实现动态代理?这时候就需要用到cglib了。这种代理方式就叫做cglib代理。
cglib代理也叫作子类代理,他是通过在内存中构建一个子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,然后加入自己需要的操作。因为使用的是继承的方式,所以不能代理final 类。

1.我们需要引入cglib的依赖

        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.5</version>
        </dependency>

2.创建一个代理类工厂并实现接口MethodInterceptor

public class ProxyFactory  implements MethodInterceptor {

    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }
    //获取代理对象
    public Object getProxy(){
        Enhancer enhancer=new Enhancer();
        //指定被代理对象的父类
        enhancer.setSuperclass(target.getClass()); //该代码
        //指定回调类
        enhancer.setCallback(this);
        //创建代理对象
        return enhancer.create();
    }


    //当代理对象执行代理方法时触发的方法
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("***********cglib方法运行前**************");
        Object r = method.invoke(target, args);
        System.out.println("===========cglib方法运行后================");
        return r;
    }
}

3.测试(我们还以数学计算为例子)

public class Test {
    public static void main(String[] args) {
        MathServiceImpl mathService=new MathServiceImpl();
        ProxyFactory proxyFactory=new ProxyFactory(mathService);
        MathServiceImpl proxy = (MathServiceImpl) proxyFactory.getProxy();
        double div = proxy.div(15, 5);
        System.out.println(div);
    }
}

4.结果

五、在SSM框架中使用AOP(记录登录日志信息)

1.dao层相关内容LoginLog


Integer AddLoginLog(LoginLog loginLog);


<!--向登录日志表中添加登录日志信息-->
    <insert id="AddLoginLog">
        insert into tbl_loginlog values (null,#{login_name},#{ipaddr},#{status},#{msg},now())
    </insert>

2.Service层

//向登录日志文件中添加数据
    public Integer AddLoginLog(LoginLog loginLog) {
        Integer integer = loginMapper.AddLoginLog(loginLog);
        return integer;
    }

 3.entity封装登录日志实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginLog {
    private String login_name;
    private String ipaddr;
    private Integer status;
    private String msg;
}

4.Aop切面文件

@Component
@Aspect
public class LoginLogAop {
    @Autowired
    private LoginService loginService;
    @AfterReturning(value = "execution(* com.five.controller.LoginController.login(..))",returning = "r")
    public void LoginAfterReturn(JoinPoint joinPoint,Object r){
        //获取登录账号
        Object[] args=joinPoint.getArgs();
        String staffName = (String) args[0];
        //获取request对象
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        //获取登录IP
        String Ip = request.getRemoteAddr();
        //获取登录时的状态
        CommonResult result=(CommonResult) r;
        //获取状态
        Integer code = result.getCode();
        //获取登录的消息
        String msg = result.getMsg();
        LoginLog loginLog=new LoginLog(staffName,Ip,code,msg);
        loginService.AddLoginLog(loginLog);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值