preface
I recently wanted to learn Spring source code, a variety of design patterns used in Spring source code incisively and vividly, I have to sigh that the original majority of developers have been standing on the shoulders of the great people programming. When it comes to Spring, interviews often ask questions about its two cores, IOC and AOP. IOC is essentially a container for bean reflection and dependency injection, managing the life cycle of beans. AOP is essentially a dynamic proxy. Today I will talk about dynamic proxy. I will explain dynamic proxies from the following aspects:
- Static agent
- JDK dynamic proxy
- CGlib dynamic proxy
- Talk about my own pitfalls with AOP in real projects
Static agent
Static proxy is very simple, we will write in the code of this kind of static proxy. In simple terms, the proxy class is passed as a parameter to the proxy class constructor, allowing the proxy class to implement more powerful functions for the proxy class.
1package com.bingo.designPatterns.proxy;
2
3/ * *
4* Description: Static proxy
5 * User: bingo
6* /
7
8public class StaticProxyTest {
9
10 public static void main(String[] args) {
11
12 UserService userService = new UserService();
13
14 LogProxy logProxy = new LogProxy(userService);
15 logProxy.addUser();
16 logProxy.deleteUser();
17 }
18}
19
20interface IUserService{
21 void addUser(a);
22 void deleteUser(a);
23}
24
25
26class UserService implements IUserService{
27
28 @Override
29 public void addUser(a) {
30 System.out.println("Add user");
31 }
32
33 @Override
34 public void deleteUser(a) {
35 System.out.println("Delete user");
36 }
37}
38
39// Log agent
40class LogProxy implements IUserService{
41
42 / / the target class
43 private UserService target;
44
45 public LogProxy(UserService target){
46 this.target = target;
47 }
48
49 @Override
50 public void addUser(a) {
51 System.out.println("Logging started");
52 target.addUser();
53 System.out.println("Logging finished");
54 }
55
56 @Override
57 public void deleteUser(a) {
58 System.out.println("Logging started");
59 target.deleteUser();
60 System.out.println("Logging finished");
61 }
62}
Copy the code
Although static proxy implementation is relatively simple, in actual projects we need to write a proxy class for each class, which requires a lot of redundant code, which is not conducive to code decoupling and extension. Dynamic proxies, however, solve the above problem by truly decoupling the business logic code from the enhanced functionality code.
A dynamic proxy
There are two main dynamic proxies used in Spring source code, JDK dynamic proxy and CGLib dynamic proxy. The main differences between the two are:
- JDK dynamic proxies typically generate proxies for classes that implement interfaces. (This can be better understood when talking about pits encountered with AOP.)
- If the target object does not implement an interface, the CGLIB proxy is used by default. If the target object implements the interface, it can be forced to use the CGLIB implementation proxy (adding the CGLIB library).
Similarities:
- Both dynamic proxies are essentially bytecode assemblies
Application scenarios of AOP dynamic proxy:
- The log
- The transaction
- permissions
- The cache
- Lazy loading
JDK dynamic proxy
JDK dynamic proxy proxy classes typically need to implement interfaces
1package com.bingo.designPatterns.proxy;
2
3import java.lang.reflect.InvocationHandler;
4import java.lang.reflect.Method;
5import java.lang.reflect.Proxy;
6
7/ * *
8* Description: JDK dynamic proxy
9 * User: bingo
10* /
11public class JdkProxyTest {
12
13 public static void main(String[] args) {
14 IPersonService personService = JdkDynamicProxy.getProxy();
15 personService.addPerson();
16 personService.deletePerson();
17 }
18}
19
20interface IPersonService{
21 void addPerson(a);
22 void deletePerson(a);
23}
24
25class PersonService implements IPersonService{
26 @Override
27 public void addPerson(a) {
28 System.out.println("Add people");
29 }
30
31 @Override
32 public void deletePerson(a) {
33 System.out.println("Delete people");
34 }
35}
36
37
38/ * *
39* newProxyInstance
40* ClassLoader loader: specifies the ClassLoader used by the current target object. The method for obtaining the loader is fixed
41* Class<? >[] interfaces: Specifies the type of the interface implemented by the target object, using generics to confirm the type
42InvocationHandler: Specifies the dynamic handler that fires the event handler's method when executing the target object's method
43* /
44class JdkDynamicProxy{
45
46 public static IPersonService getProxy(a){
47
48 IPersonService personService = new PersonService();
49
50 IPersonService proxy = (IPersonService) Proxy.newProxyInstance(IPersonService.class.getClassLoader(), newClass<? >[]{IPersonService.class},new InvocationHandler() {
51
52 @Override
53 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
54 System.out.println("Logging started");
55 Object obj = method.invoke(personService, args);
56 System.out.println("Logging finished");
57 return obj;
58 }
59 });
60
61 return proxy;
62 }
63}
Copy the code
CGLib dynamic proxy
You need to import the cglib.jar, asm.jar package to use this agent
1package com.bingo.designPatterns.proxy;
2
3import net.sf.cglib.proxy.Enhancer;
4import net.sf.cglib.proxy.MethodInterceptor;
5import net.sf.cglib.proxy.MethodProxy;
6
7import java.lang.reflect.Method;
8
9/ * *
10* Description: Cglib dynamic proxy
11 * User: bingo
12* /
13
14public class CglibProxyTest {
15 public static void main(String[] args) {
16
17 CglibProxy proxy = new CglibProxy();
18 Train t = (Train)proxy.getProxy(Train.class);
19 t.move();
20 }
21}
22
23class Train {
24
25 public void move(a){
26 System.out.println("The train is moving...");
27 }
28}
29
30class CglibProxy implements MethodInterceptor {
31
32 private Enhancer enhancer = new Enhancer();
33
34 public Object getProxy(Class clazz){
35 // Set the class to create the subclass
36 enhancer.setSuperclass(clazz);
37 enhancer.setCallback(this);
38
39 return enhancer.create();
40 }
41
42 / * *
43Intercepts all calls to target class methods
44* obj an instance of the target class
45* m Reflection object of the target method
46* Arguments to the args method
47* proxy Instance of the proxy class
48* /
49 @Override
50 public Object intercept(Object obj, Method m, Object[] args,
51 MethodProxy proxy) throws Throwable {
52 System.out.println("Log start...");
53 // The proxy class calls the parent class's methods
54 proxy.invokeSuper(obj, args);
55 System.out.println("Log end...");
56 return null;
57 }
58}
Copy the code
Potholes encountered in projects using AOP
Problems encountered by the author in the development of applets service interface: 1. The transaction manager is configured in the Spring configuration file, as follows:
1<! -- Transaction manager configuration, single data source transaction -->
2<tx:annotation-driven transaction-manager="transactionManager" />
3<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
4 <property name="dataSource" ref="dataSource" />
5</bean>
Copy the code
2. After configuring the transaction manager, add the @Transactional annotation
1@Service
2@Transactional
3public class AccountService{
4 //to do something
5}
Copy the code
At first glance, there was nothing wrong with this configuration and use, right? But as soon as the project was put into the Tomcat service, there was an error. I can’t remember exactly what error was reported, but I do remember that the error was caused by the transaction annotation. I found that various configurations were not bad, and I did not know where the problem was. But the original project started normally, but my project reported an error. A closer look reveals that services in the company’s original projects defined an interface as a specification, and the @Transactional annotation was added to the interface implementation class. As a result, I vaguely define an interface specification for each Service class that implements the @Transactional annotation as follows:
1@Service
2@Transactional
3public class AccountService implements IAccountService {
4 //to do something
5}
Copy the code
The configuration remains the same, you simply define and implement the interface for the Service layer, and the project runs fine.
What is the root of the problem? Here it is:
1<tx:annotation-driven transaction-manager="txManager"/>
Copy the code
JDK dynamic proxies are enabled by default. The JDK can only proxy interfaces, not classes. My project uses this configuration, but the project starts with an error because the Service interface is not defined.
To use the CGLIB dynamic proxy, perform the following configuration:
1<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true"/>
Copy the code
I don’t know if any readers have encountered the same problem as me, but if you don’t understand dynamic proxies, you might have the same problem. However, it is very important to write program specifications. In the MVC three-tier structure, except the Controller layer, the Service and Dao layer all need to define interfaces, which are the specifications developed by enterprises. At the time was a just graduate interns, hard to avoid can sometimes write this non-standard code, but hope I can be more and more specification, see the alibaba Java development’s handbook, look at the source code, see Daniel to write code, believe oneself also can write soon so elegant code, programming into a kind of art.
I am Xiaobin, a Java programmer in Guangzhou. I just graduated for half a year and have been devoted to learning Java. If you are interested in my article, you can follow my wechat official account (J2 Binbin).