0. 代理模式 (AOP的原理)
代理模式
Rent:
//抽象角色:租房
public interface Rent {
public void rent();
}
Host:
//真实角色: 房东,房东要出租房子
public class Host implements Rent{
public void rent() {
System.out.println("房屋出租");
}
}
Proxy:
//代理角色:中介
public class Proxy implements Rent {
private Host host;
public Proxy() { }
public Proxy(Host host) {
this.host = host;
}
//租房
public void rent(){
seeHouse();
host.rent();
fare();
}
//看房
public void seeHouse(){
System.out.println("带房客看房");
}
//收中介费
public void fare(){
System.out.println("收中介费");
}
}
Client:
//客户类,一般客户都会去找代理!
public class Client {
public static void main(String[] args) {
//房东要租房
Host host = new Host();
//中介帮助房东
Proxy proxy = new Proxy(host);
//你去找中介!
proxy.rent();
}
}
分析:在这个过程中,你直接接触的就是中介,就如同现实生活中的样子,你看不到房东,但是你依旧租到了房东的房子通过代理,这就是所谓的代理模式。
静态代理的好处:
- 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
- 公共的业务由代理来完成 . 实现了业务的分工 ,
- 公共业务发生扩展时变得更加集中和方便 .
缺点 :
- 类多了 , 多了代理类 , 工作量变大了 . 开发效率降低 .
1. 代理模式
若有userservice 实现增删查改:
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
public class UserServiceImp implements UserService{
public void add() {
System.out.println("Add User");
}
public void delete() {
System.out.println("Delete User");
}
public void update() {
System.out.println("Update User");
}
public void query() {
System.out.println("Query User");
}
}
若是在 add、delete、update、query 添加日志:输出“data operation”.在每个语言中就重复的相同语句。一个好的方法是将这些额外的操作方法“代理中去”。UserService 专注于相应的操作,其余日志等操作交给Proxy来完成!
UserServiceProxy:
public class UserServiceProxy implements UserService{
//UserService 就是被代理的对象
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
public void add() {
log("add");
userService.add();
}
public void delete() {
log("delete");
userService.delete();
}
public void update() {
log("update");
userService.update();
}
public void query() {
log("query");
userService.query();
}
public void log(String msg){
System.out.println("Add "+ msg +" operation");
}
}
其中 UserServiceImp 被USerServiceImp代理了。
修改原有代码是大忌。
优点:
- 使真实的操作专注,将公告的操作交给代理
- 可以方便拓展,原有代码不变
缺点:
- 一个角色一个代理,若是角色过多,代理也就过多了。
开发一般流程
2. AOP
AOP(面向切面编程):通过预编译方
式和运行期动态代理实现程序功能的统一维护的一种技术。
- 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 ....
- 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。(比如Logger类)
- 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。(Logger类中的一个方法:log())
- 目标(Target):被通知对象。
- 代理(Proxy):向目标对象应用通知之后创建的对象。(代理类)
- 切入点(PointCut):切面通知 执行的 “地点”的定义。(在哪里执行)
- 连接点(JointPoint):与切入点匹配的执行点。(在哪里执行)
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:
2.2 AOP配置(基于Spring的接口实现)
想要使用AOP织入,需要在pox.xml导入一个依赖包!
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.10</version>
</dependency>
代码:(上面的案例)
- Spring的API接口
UserService 和 UserServiceImp代码如上。
定义Log 和 AfterLog
// 定义Log 继承MethodBeforeAdvice 接口
//
public class Log implements MethodBeforeAdvice {
// method: 要执行目标函数的方法
//object :参数
// targert: 目标对象。(被代理的)
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println( target.getClass().getName() +"的" + method.getName() +" 被执行了");
}
}
// 返回结果
public class AfterLog implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了"+method.getName() +" 方法; 返回结果为 :" + returnValue);
}
}
在Bean.xml 中
<?xml version="1.0" encoding="UTF-8"?>
<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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="com.service.UserServiceImp"/>
<bean id="log" class="com.log.Log"/>
<bean id="afterLog" class="com.log.AfterLog" />
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.service.UserServiceImp.*(..))"/>
<!-- log所在的类已经实现了MethodBefore、After等接口的方法,这里直接定位到Log类中的方法; afterLog 同理; advice 对应的方法-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut" />
</aop:config>
</beans>
Test:
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 动态代理的是接口, 要使用 UserService ,而不是 UserServiceImp
UserService userService = (UserService) context.getBean("userService");
userService.add();
}
- 在 <aop:config></config> 配置
- <aop:pointcut> 就是切入点(定义的),也就是在哪里插入;expression就是要代理的方法;
- <aop:advisor> 通知,也就是自己定义的,然后引用切入点,pointcut-ref=“pointcut”
2.2 定义类实现AOP
自定义切面
public class DiyPointcut {
public void methodeBefore(){
System.out.println("method before");
}
public void methodAfter(){
System.out.println("method before");
}
}
在Beans.xml 中
<bean id="userService" class="com.service.UserServiceImp"/>
<bean id="log" class="com.log.Log"/>
<bean id="afterLog" class="com.log.AfterLog" />
<bean id="diy" class="com.diy.DiyPointcut"/>
<aop:config>
<!--自定义切面, ref 引用到类-->
<aop:aspect ref="diy">
<!-- 切入点-->
<aop:pointcut id="pointcut" expression="execution(* com.service.UserServiceImp.*(..))"/>
<!-- 通知-->
<aop:before method="methodAfter" pointcut-ref="pointcut"/>
<aop:after method="methodeBefore" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
- 其中,<aop:aspect> 切面,对象为类(我么自定义的),通过 ref 引用
- <aop:pointcut> 切入点
- <aop:before> 在方法之前执行;对比上面的Log,这个方法放在Log里面类中执行的
- <aop:brefore> 同样的道理
Test:
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 动态代理的是接口, 要使用 UserService ,而不是 UserServiceImp
UserService userService = (UserService) context.getBean("userService");
userService.add();
}
2.3 注解实现AOP
@Aspect
public class AnnotationPoint {
@Before("execution(* com.service.UserServiceImp.*(..))")
public void before(){
System.out.println("opeation before");
}
}
- @Aspect 使用注解设置切面(对上小姐的 <aop:aspect>)
- @Before 对应 <aop:before> (通知:advice),里面的参数对应切入点(pointcut)。
使用注解在 Beans.xml, 需要开启注解:
<aop:aspectj-autoproxy />
<bean id="annotationPointcut" class="com.diy.AnnotationPoint"/>
<aop:aspectj-autoproxy />
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。