Source: https://zhuanlan.zhihu.com/p/63126398
Introduction to JDK dynamic proxy, today and everyone to be a small case: an analog Spring's transaction management.
main content:
- a familiar stranger
- Cottage AOP transaction needs analysis
- AOP transaction specific code implementation
a familiar stranger
If the interviewer asks, "Please talk about your understanding of Spring" and estimated that many people will blurt out: IOC and AOP. IOC is probably the most immediate impression on everyone Spring, is a large container, installed a lot of bean, it will help you to do dependency injection.
IOC
But for AOP, many people actually do not have much concept of time do not know where to use Spring AOP. As if the transaction with the cut, but not the specific understanding. Well, I ask you a question, I wrote a UserController, and UserServiceImpl implements UserService, and injecting Service layer objects in the UserController:
@Autowired
private UserService userService;
Well, this must be userService UserServiceImpl instance we write it?
If you do not understand what I want to ask, explain your own understanding of Spring's still too limited IOC.
In fact, Spring dependency injection is not necessarily an object is an instance of the class to write our own, it may be userServiceImpl proxy object. The following are two cases demonstrate this:
- Object injection userServiceImpl
Injection type is UserServiceImpl
- Injecting a proxy object userServiceImpl (CGLib dynamic proxies)
CGLib agent is injected dynamically generated proxy object userServiceImpl
Why two injections of different objects?
Because the second time I UserServiceImpl added @Transactional comment.
Spring at this time to read this comment, we know that we want to use transactions. And UserService class we write and does not contain any code related to the transaction. If you, how would you do? Dynamic proxies Well!
But to use the dynamic management agent to complete the transaction, but also you need to write a notification class, and the incoming notification object proxy object, in charge of affairs of the notification and submission open, and invoke the target object method of the same name to complete the business function within the proxy object.
We can think of the program, Spring certainly know. Similarly, Spring in order to achieve affairs, also wrote a notification class, TransactionManager. When creating dynamic proxy object proxy, Spring will transactionManager weaving proxy object, the proxy object is then injected into the UserController.
So userService we use in UserController in fact a proxy object, the proxy object only support transactions.
Cottage AOP transaction needs analysis
Spring understand the general process of the transaction, we come to analyze how to write their own affairs AOP a cottage.
AOP transaction, there are two concepts: AOP and transactions.
Affairs, everyone is familiar with, here mainly to talk about what is AOP. AOP, which is the abbreviation of Aspect-Oriented Programming (Oriented Programming) is. What is Aspect Oriented Programming? Sometimes directly introduce what a thing that may be more difficult. But it is doing when it comes, it will immediately take the hint of.
Our system, there is often a cross-business, such as transaction logs and so on. UserService of method1 use to it, BrandService of method2 have to use it. A cross cut into the business is to one aspect of the system. With a specific code shows that:
This aspect can be log, the transaction may be
Cross-business problem is the programming aspect-oriented programming. AOP's business goal is to make cross-modular. Section code can be moved around the original method:
When the original without AOP, cross-business written directly before and after the internal method, with the AOP cross traffic before and after the write method call. This is the underlying AOP implementations related to: the dynamic proxy method is actually the same name as the proxy object to call the target object, and add enhanced code before and after the call. However, the two eventually run effect is the same.
The so-called modular, my personal understanding is that the section of code to make a manageable state. Printing such as logging, it is no longer hard-coded in fragmented statement in the method, but made a notification class, to execute the code through a notification section.
So, now demand has been very clear that we need a notification class (TransactionManager) to perform a transaction, a proxy factory to help generate the proxy object, and then use various methods of dynamic agent transaction code woven into the proxy object.
Like following three Service, originally did not open transactions:
We hope to eventually achieve the effect, I added a @MyTransactional later, I returned to a proxy factory proxy object:
Acting factory using dynamic proxy to create a proxy object for each target audience
Detailed analysis:
txManager fact, before and after the target object test () method before and after the execution of a transaction, rather than internal method
That is, the proxy object transaction method = + target object method.
In addition, there is a thorny issue: the transaction, you must use the same Connection object. How to ensure? After the first obtain a Connection object from the data source and open transactions, deposit it in the current thread ThreadLocal, until a DAO layer, or taken from ThreadLocal, so we can guarantee the open Connection object Affairs and operation of the database using the same One.
After opening Affairs, Controller not directly call our own to write the Service, but Spring provides a proxy object
This is the realization of the principle of affairs.
AOP transaction specific code implementation
ConnectionUtils Tools
Package com.demo.myaopframework.utils; Import org.apache.commons.dbcp.BasicDataSource; Import the java.sql.Connection; / ** * connecting tools, which is used to obtain a connection from the data source, and implemented, and binding thread * / public class ConnectionUtils { Private the ThreadLocal <connection> TL = new new the ThreadLocal <connection> (); Private static of BasicDataSource the dataSource = new new of BasicDataSource (); // static code block, set the parameters of the database connection static { the dataSource. setDriverClassName ( "com.mysql.jdbc.Driver" ); dataSource.setUrl ("JDBC: MySQL: // localhost: 3306 / Test" ); dataSource.setUsername ( "the root" ); dataSource.setPassword ( "123456" ); } / ** * Get the current thread connection * @return * / public getThreadConnection connection () { the try { // the start ThreadLocal 1. Get connection Conn = tl.get (); // 2. determining whether the current thread connection iF (Conn == null ) { // 3. data from obtaining a source connection, and stored in a ThreadLocal Conn = dataSource.getConnection (); tl.set (Conn); } // 4. return connection on the current thread return Conn; } the catch (Exception E) { the throw new new a RuntimeException (E); } } / ** * connecting thread and unbundling * / public void removeConnection () { tl.remove (); } }
AOP notice (Transaction Manager)
Package com.demo.myaopframework.utils; / ** * transaction management and related tools, which contains, open transaction, committing the transaction, and transaction rollback releasable connection * / public class the TransactionManager { Private ConnectionUtils connectionUtils; public void setConnectionUtils (connectionUtils connectionUtils) { the this .connectionUtils = connectionUtils; } / ** * open transaction * / public void beginTransaction () { the try { connectionUtils.getThreadConnection () the setAutoCommit (. to false ); } the catch (Exception E) { e.printStackTrace (); } } / ** * commit transaction * / public void the commit () { the try { connectionUtils.getThreadConnection () the commit ();. } The catch (Exception E) { e.printStackTrace (); } } / ** * rollback transaction * / public void ROLLBACK () { the try { connectionUtils.getThreadConnection () ROLLBACK ();. } the catch (Exception E) { e.printStackTrace (); } } / ** * releasable connection * / public void Release () { the try { connectionUtils.getThreadConnection () Close ();. // is also connected back to the pool connectionUtils.removeConnection (); } the catch (Exception E) { e.printStackTrace (); } } }
Custom annotation
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface MyTransactional { }
Service
public interface UserService { void getUser(); } public class UserServiceImpl implements UserService { @Override public void getUser() { System.out.println("service执行..."); } }
Examples factory
public class the BeanFactory { public Object the getBean (String name) throws Exception { // get the object target class Class Class clazz = <?> the Class.forName (name); // get the target object Object the bean = clazz.newInstance (); / / get @MyTransactional comment on the target class MyTransactional myTransactional = clazz.getAnnotation (MyTransactional. class ); // if hit @MyTransactional notes, return proxies, otherwise the target object IF ( null ! = myTransactional) { the ProxyFactoryBean the ProxyFactoryBean = new newThe ProxyFactoryBean (); the TransactionManager txManager = new new the TransactionManager (); txManager.setConnectionUtils ( new new ConnectionUtils ()); // assembly and notification audiences proxyFactoryBean.setTxManager (txManager); proxyFactoryBean.setTarget (the bean); Object proxyBean = proxyFactoryBean.getProxy ( ); // return proxies return proxyBean; } // return the target object return the bean; } }
Acting factory
public class the ProxyFactoryBean { // notify Private the TransactionManager txManager; // audiences Private Object target; public void setTxManager (the TransactionManager txManager) { the this .txManager = txManager; } public void setTarget (Object target) { the this .target = target; } / / incoming audience target, it notifies assembled, the proxy object returned public Object The getProxy () { Object proxy = the Proxy.newProxyInstance ( target.getClass (). getClassLoader (), / * 1. classloader* / Target.getClass (). The getInterfaces (), / * 2. interfaces implemented by the target object * / new new of InvocationHandler () { / * 3.InvocationHandler * / @Override public Object Invoke (Object Proxy, Method, Method, Object [] args) throws the Throwable { the try { // 1. open transaction txManager.beginTransaction (); // 2. perform operations Object retVal =Method.invoke (target, args); // 3. commit the transaction txManager.commit (); // 4. return result return retVal; } the catch (Exception E) { // 5. The rollback transaction txManager.rollback (); the throw new new a RuntimeException (E); } the finally { // 6. The releasable connection txManager.release (); } } } ); return Proxy; } }
Code structure
Get ordinary UserService:
UserServiceImpl @MyTransactional to add annotations, proxied: