6、代理模式及AOP
10、代理模式
10.1、静态代理
1、代理模式的优点
也就是为什么用代理模式?
- 代理模式符合类的构造原则,使类职责分明
- 就拿下面个例子来说:房东只有租房子方法,顾客找代理,代理可以做代理该做的事情
- 使真实类更加纯粹
- 接口
public interface Rent {
void rentHouse();
}
2、被代理的对象
public class Host implements Rent{
private String name;
public Host(String name) {
this.name = name;
}
public void rentHouse() {
//房东只做一件事就是租房子,他不带客户看房子什么的
System.out.println("房东"+name+"出租房子!");
}
}
3、静态代理
package bing;
/**
* @author zhangbingbing
* @version 1.0
* @date 2020/5/18 17:03
*/
public class Proxy implements Rent {
private Host host;
public void setHost(Host host) {
this.host = host;
}
//代理就可以做很多事情了
public void rentHouse() {
//还是调用房租的租房子方法
lookHouse();
host.rentHouse();
hetong();
cost();
}
private void lookHouse() {
System.out.println("带顾客看房子");
}
private void hetong() {
System.out.println("签合同");
}
private void cost() {
System.out.println("收房租");
}
}
4、顾客
public class Client {
//客户要去租房子
public static void main(String[] args) {
Host host = new Host("bingbing"); //一定要有哥真正的房租
Proxy proxy = new Proxy();
proxy.setHost(host);
proxy.rentHouse();
}
}
各个类的值则分明,增加了效率和可读性
- 在实际开发中的用处
直接在service层加了一个UserService代理,在不改变原来类的基础上增加了日志功能!
package bing.demo2.service;
/**
* @author zhangbingbing
* @version 1.0
* @date 2020/5/18 17:48
*/
public class UserProxy implements UserService {
private UserServiceImpl userService;
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
public void add() {
log();
userService.add();
}
public void delete() {
log();
userService.delete();
}
public void update() {
log();
userService.update();
}
public void query() {
log();
userService.query();
}
private void log() {
System.out.println("打印了日志");
}
}
2、动态代理
JDK实现动态代理需要两个类:
- 反射包下的Proxy : 用来返回要代理的实例
- InvocationHandler : 执行代理的方法
-
代码
-
动态代理:可以代理任何类
优点:我们不需要像静态代理一个类型的类必须跟一个代理,这里可以说是一个工具类,每一个类都可以拿来用,
底层利用反射的机制实现,而且静态代理必须和实例实现同一个接口,而有些方法代理并不需要实现,增加了代码冗余。
package bing.demo3;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author zhangbingbing
* @version 1.0
* @date 2020/5/23 19:01
*/
public class ProxyInvocationHandler implements InvocationHandler {
private Object target;
public void setTarget(Object target) {
this.target = target;
}
public Object getProxy() {
//关键:我们在这里返回被代理对象的实例
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
//显然invoke就是用来执行方法的
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//用反射来实现
Object invoke = method.invoke(target, args);
return invoke;
}
}
- 测试
public class Client {
public static void main(String[] args) {
//都需要一个代理的实例对象
Host host = new Host("小张");
ProxyInvocationHandler phl = new ProxyInvocationHandler();
phl.setTarget(host);//设置这个实例对象
Rent proxy = (Rent) phl.getProxy(); //返回代理类
proxy.rentHouse(); //执行代理方法
}
}
11、AOP
- 概念:
面向切面编程:含义是公司要求在原来的代码上做添加一些日志啊什么功能,这时候我们就可以增加一层切面来进行实现
上一讲中我们讲解了代理模式,这是AOP的基础,一定要先搞懂它
那我们接下来就来聊聊AOP吧!
什么是AOP
AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
Aop在Spring中的作用
提供声明式事务;允许用户自定义切面
以下名词需要了解下:
- 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 ....
- 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
- 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
- 目标(Target):被通知对象。
- 代理(Proxy):向目标对象应用通知之后创建的对象。
- 切入点(PointCut):切面通知 执行的 “地点”的定义。
- 连接点(JointPoint):与切入点匹配的执行点。
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:
即 Aop 在 不改变原有代码的情况下 , 去增加新的功能 .
1、在Spring中实现aop
- 方式一:通过Spring API来实现
特点:功能比较强大:可以获取执行方法的类名,方法名,方法的返回值
主要是实现接口:MethodBeforeAdvice
package log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
/**
* @author zhangbingbing
* @version 1.0
* @date 2020/5/23 22:35
*/
public class PreviousLog implements MethodBeforeAdvice {
//我们解释一下参数:method:顾名思义就是所执行方法的前面 objects:参数 o : 执行方法的对象
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(o.getClass().getName() + "将要执行" + method.getName());
}
}
package log;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
/**
* @author zhangbingbing
* @version 1.0
* @date 2020/5/23 22:40
*/
public class AfterLog implements AfterReturningAdvice {
/**
*
* @param o :执行方法后返回值
* @param method : 要执行的方法
* @param objects : 参数
* @param o1 : 执行方法的对象
* @throws Throwable
*/
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println(o1.getClass().getName() + "执行好了" + method.getName() + "方法,返回值为" + o);
}
}
-
方法二,自定义实现
-
定义一个很普通的类
public class MyAOP {
public void before() {
System.out.println("before............");
}
public void after() {
System.out.println("after.............");
}
}
- 设置xml,命名空间
<bean id="myAOP" class="self.MyAOP"/>
<aop:config>
<!-- 设置切面-->
<aop:aspect ref="myAOP">
//切点
<aop:pointcut id="point" expression="execution(* bing.UserServiceImpl.*(..))"/>
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
3、使用注解开发AOP
在知道的类上添加
@Aspect
在xml配置文件中指定使用注解
<!-- 一定要说明使用了注解-->
<aop:aspectj-autoproxy/>
java
package self;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
/**
* @author zhangbingbing
* @version 1.0
* @date 2020/5/24 14:22
*/
@Aspect
public class AchieveAOPByAnno {
public AchieveAOPByAnno() {
System.out.println("AchieveAOPByAnno");
}
@Before("execution(* bing.UserServiceImpl.*(..))")
public void before() {
System.out.println("before--------");
}
@After("execution(* bing.UserServiceImpl.*(..))")
public void after() {
System.out.println("after-------------");
}
//环绕
@Around("execution(* bing.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
//关键代码:执行方法
Object proceed = jp.proceed();
//环绕后
System.out.println("环绕后");
System.out.println(proceed);
}
}