0%

Idea控制Java版本

1. 包管理中

  • Project

  • Modules

两个地方都有设置Java版本的位置。

2. 偏好设置中

Cmd + ,

Java Compiler中也可以指定Java版本

Maven控制Java版本

如果是Maven项目,很有可能是因为Maven默认的Java的版本而出错。

pom.xml中进行配置

1
2
3
4
5
6
7
8
9
10
11
12
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
</plugins>
</build>

只要刷新Maven项目,就可以解决Java版本问题。

注解实现AOP

配置类

1
2
3
4
5
6
7
8
9
10
11
12
@Configuration
@EnableAspectJAutoProxy
public class AnnoAOPConfig {
@Bean
public LogAnno logAnno() {
return new LogAnno();
}
@Bean
public LoginService loginService() {
return new LoginServiceImpl();
}
}

注意@EnableAspectJAutoProxy开启自动Proxy

如果是使用xml进行配置,也需要开启自动Proxy。

业务类应该返回相应的接口,动态路由的底层实现即是对接口进行调用。

切面类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Aspect
@Component
public class LogAnno {
@Before("execution(* com.edlison.design.spring.aop.style_anno.LoginServiceImpl.*(..))")
public void before() {
System.out.println("[Log] Before...");
}
@After("execution(* com.edlison.design.spring.aop.style_anno.LoginServiceImpl.*(..))")
public void after() {
System.out.println("[Log] After...");
}
@Around("execution(* com.edlison.design.spring.aop.style_anno.LoginServiceImpl.*(..))")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("[Log] Point Start");
joinPoint.proceed();
System.out.println("[Log] Point End");
System.out.println("joinPoint.getSignature() = " + joinPoint.getSignature());
}
}

@Aspect实际上是引用的org.aspectj包。

切面类@Component注册为Bean

问题

  • 执行先后顺序与视频中的不一样,与Python装饰器的不一样,使用配置类的原因?
  • @Component@Bean重复?

自定义切面实现AOP

通过切面来实现AOP

Aspect

1
2
3
4
5
6
7
8
public class CustomAspect {
public void before() {
System.out.println("[Log] Before...");
}
public void after() {
System.out.println("[Log] After...");
}
}

配置文件

1
2
3
4
5
6
7
8
9
<aop:config>
<aop:aspect ref="customAspect">
<aop:pointcut id="first"
expression="execution(* com.edlison.design.spring.aop.style_api.service.UserServiceImpl.*(..))"/>

<aop:before method="before" pointcut-ref="first"/>
<aop:after method="after" pointcut-ref="first"/>
</aop:aspect>
</aop:config>

这种方式更加简便,只需要定义一个切面类,类中的方法即可通过配置,来对切点进行操作,不再需要繁琐的实现接口。

但是无法实现更复杂的操作,比如打印调用的切点方法,类名等。

术语

Spring AOP最重要的是可以让用户自定义切面

![image-20210202221824940](/Users/edlison/Library/Application Support/typora-user-images/image-20210202221824940.png)

使用Spring的API接口实现AOP

需要实现的接口

![image-20210202221950162](/Users/edlison/Library/Application Support/typora-user-images/image-20210202221950162.png)

前置通知

1
2
3
4
5
6
7
public class LogBefore implements MethodBeforeAdvice {
@Override
// method: 要执行的目标对象的方法,objects: 参数,o: 目标对象
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("[Log Before]ClassName: " + o.getClass().getSimpleName() + " MethodName: " + method.getName());
}
}

后置通知

1
2
3
4
5
6
public class LogAfter implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("[Log After]ClassName: " + target.getClass().getSimpleName() + " MethodName: " + method.getName());
}
}

配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="com.edlison.design.spring.aop.style_one.service.UserServiceImpl"/>
<bean id="logBefore" class="com.edlison.design.spring.aop.style_one.log.LogBefore"/>
<bean id="logAfter" class="com.edlison.design.spring.aop.style_one.log.LogAfter"/>
<aop:config>
<aop:pointcut id="first"
expression="execution(* com.edlison.design.spring.aop.style_one.service.UserServiceImpl.* (..))"/>
<aop:advisor advice-ref="logBefore" pointcut-ref="first"/>
<aop:advisor advice-ref="logAfter" pointcut-ref="first"/>
</aop:config>
</beans>

配置文件中,首先注册Bean,引入AOP约束,对AOP进行配置。

  • 定义切入点(PointCut).
  • 写表达式(Expression).
  • 配置通知器(advisor),并将其与切入点绑定。

通知器执行前后,完全靠实现的接口决定。

注意

表达式见Google

引入

相较于静态代理模式,所有的方法动态生成。

这里介绍基于JDK的方式实现动态代理。

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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);
}

// 唤醒方法 返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
Object result = method.invoke(target, args);
return result;
}

// 定义唤起方法执行前后进行装饰的方法
private void log(String msg) {
System.out.println("[Log] " + msg);
}
}

Proxy

返回代理类,需要传入:

  • 当前作为Proxy的Class加载器.
  • 需要代理的对象的接口
  • 自定义的InvocationHandler,实现了InvocationHandler接口。
1
2
3
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}

InvocationHandler

唤起事务处理。

1
2
3
4
5
6
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
Object result = method.invoke(target, args);
return result;
}

实现了InvocationHandler,需要重写invoke方法。

使用反射的方法执行method.invoke传入

  • 被代理的接口(target)
  • 执行时的形参(args)

在唤起执行方法前后,可以加入自定义的事务处理

自定义

可以实现将一个ProxyInvocationHandler分成一个CustomProxy和一个CustomInvocationHandler

CustomInvocationHandler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class CustomInvocationHandler implements InvocationHandler {

private IndexService indexService;

public CustomInvocationHandler(IndexService indexService) {
this.indexService = indexService;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Proxy的作用?
logBefore(method.getName());
Object res = method.invoke(indexService, args);
logAfter(indexService.getClass().getName());
return res;
}

// 以下定义 前后装饰方法
private void logBefore(String msg) {
System.out.println("[Log] " + msg);
}

private void logAfter(String msg) {
System.out.println("[Log] " + msg + "has been done!");
}
}

CustomProxy

1
2
3
4
5
6
public class CustomProxy {

public Object getProxy(IndexService indexService, InvocationHandler invocationHandler) {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), indexService.getClass().getInterfaces(), invocationHandler);
}
}

Client

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class CustomClient {
public static void main(String[] args) {
// 需要代理的接口
IndexService indexService = new IndexServiceImpl();
// 实例化 自定义代理接口需要进行的处理类
CustomInvocationHandler customInvocationHandler = new CustomInvocationHandler(indexService);
// 实例化 自定义的代理类
CustomProxy customProxy = new CustomProxy();
// 获取代理 需要传入代理的接口及自定义的处理类
IndexService proxy = (IndexService) customProxy.getProxy(indexService, customInvocationHandler);
// 执行测试
proxy.add();
}
}

ProxyInvocationHandler可以分离。

  • InvocationHandler必须要传需要代理的接口。
  • Proxy必须要传需要代理的接口,以及自定义的Handler

问题

ProxyInvocationHandler可以解藕,但是他们都需要被代理的接口。

进行接口实现接口的设计?

InvocationHandler中实现了invoke后其中传的Proxy的作用?