For more spring-related articles, see
Spring Learning (1)
Spring Learning (2) Ambiguity of Autowiring
Fourth, spring AOP
Spring AOP (Aspect Oriented Programming) is aspect-oriented programming. In our usual oop (Object Oriented Programming, object-oriented programming), there will inevitably be some places that cannot be taken care of. For example, our log Function. In most business logic, logging is essential. Based on oop, we need to add code to enable logging when implementing the core functions of each object. These codes are not related to the business logic we need to implement, and will be reused in many places. Therefore, we hope to extract and encapsulate these logics that are not related to the business but are commonly called by multiple modules, so that in the system The amount of repetitive code will be greatly reduced, and the logic of the function will be clearer and easier to maintain.
1. Common terms
Advice
: notification, the work to be done by the aspect. There are five types of notifications in the spring aspect: Before, After, Around, After-returning, and After-throwing.Join point
: The connection point, the point of application notification, is a point that can insert the aspect, spring only supports the connection point of the method levelPoincut
: Cut point, which defines where the cut face is inserted. Intuitively speaking, it is generally reflected as a cut point expressionAspect
: Aspect, a combination of advice and pointcut, generally a classIntroduction
: introduced, allowing us to add new methods or properties to an existing classWeaving
: Weaving, the process of applying an aspect to a target object and creating a new object proxy.
2. Use annotations to create simple aspects
Enable AspectJ automatic proxying in xml files
<!--启用AspectJ自动代理 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
Note proxy-target-class="true"
that when this true
property is, it is a class-based proxy, using cglib, and when false
it is, it is an interface-based jdk dynamic proxy, and the default value is that false
we can see it through a simple class.
package com.shiqy.controller;
import com.shiqy.config.MyAspectConfig;
import com.shiqy.dao.UserDao;
import com.shiqy.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@Component
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-mybatis.xml")
//@ContextConfiguration(classes = MyAspectConfig.class)
public class MyLogAspectTest {
@Autowired
private UserService userService;
@Autowired
private UserDao userDao;
@Test
public void test() {
System.out.println(userDao.getClass());
System.out.println(userService.getClass());
}
}
At that timeproxy-target-class="false"
, the result was
class com.sun.proxy.$Proxy25
class com.sun.proxy.$Proxy27
true
When is, the result is
class com.sun.proxy.$Proxy25
class com.shiqy.service.impl.UserServiceImpl$$EnhancerBySpringCGLIB$$cc3824e3
a simple slice
package com.shiqy.service.aspect;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class MyLogAspect {
@Before("execution(* com.shiqy.service.impl.UserServiceImpl.*(..))")
public void beforeMethod(){
System.out.println("记录日志!!!");
}
@After("execution(* com.shiqy.service.impl.UserServiceImpl.*(..))")
public void afterMethod(){
System.out.println("记录成功!!!");
}
}
@AspectJ
The annotation indicates that the class is an aspect, @Before
and the annotation indicates that @After
the type of notification is pre-advice and post-advice, which execution()
is used to match the execution method that is the connection point, and execution(* com.shiqy.service.impl.UserServiceImpl.*(..))
represents UserServiceImpl
all methods of the class. The role of this aspect is when the specified implementation class is executed. Notifications will be sent before and after any method of .
In the above code, @Before
and @After
the annotated pointcut expressions are redundant, we can use @PointCut
annotations to define reusable pointcuts
package com.shiqy.service.aspect;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class MyLogAspect {
@Pointcut("execution(* com.shiqy.service.impl.UserServiceImpl.*(..))")
public void myMethod(){ }
@Before("myMethod()")
public void beforeMethod(){
System.out.println("记录日志!!!");
}
@After("myMethod()")
public void afterMethod(){
System.out.println("记录成功!!!");
}
}
Next write the test class
package com.shiqy.controller;
import com.shiqy.config.MyAspectConfig;
import com.shiqy.dao.UserDao;
import com.shiqy.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@Component
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-mybatis.xml")
//@ContextConfiguration(classes = MyAspectConfig.class)
public class MyLogAspectTest {
@Autowired
private UserService userService;
@Test
public void test(){
long id =1;
System.out.println(userService.selectUser(id).getUsername());
}
}
In spring-mybatis.xml
, we need to configure
<context:component-scan base-package="com.shiqy.service"/>
automatic scanning to get our slices, and the result of the operation is
20:52:26.701 [main] DEBUG com.mchange.v2.cfg.MConfig - The configuration file for resource identifier '/mchange-commons.properties' could not be found. Skipping.
20:52:26.702 [main] DEBUG com.mchange.v2.cfg.MConfig - The configuration file for resource identifier '/mchange-log.properties' could not be found. Skipping.
20:52:26.702 [main] DEBUG com.mchange.v2.cfg.MConfig - The configuration file for resource identifier 'hocon:/reference,/application,/c3p0,/' could not be found. Skipping.
20:52:26.702 [main] DEBUG com.mchange.v2.cfg.MConfig - The configuration file for resource identifier '/c3p0.properties' could not be found. Skipping.
20:52:26.801 [main] INFO com.mchange.v2.c3p0.C3P0Registry - Initializing c3p0-0.9.5.2 [built 08-December-2015 22:06:04 -0800; debug? true; trace: 10]
20:52:26.850 [main] DEBUG com.mchange.v2.c3p0.management.DynamicPooledDataSourceManagerMBean - MBean: com.mchange.v2.c3p0:type=PooledDataSource,identityToken=z8kflt9u1r0kgrq1ycbj82|7fd50002,name=z8kflt9u1r0kgrq1ycbj82|7fd50002 registered.
20:52:26.874 [main] DEBUG com.mchange.v2.c3p0.management.DynamicPooledDataSourceManagerMBean - MBean: com.mchange.v2.c3p0:type=PooledDataSource,identityToken=z8kflt9u1r0kgrq1ycbj82|7fd50002,name=z8kflt9u1r0kgrq1ycbj82|7fd50002 unregistered, in order to be reregistered after update.
20:52:26.874 [main] DEBUG com.mchange.v2.c3p0.management.DynamicPooledDataSourceManagerMBean - MBean: com.mchange.v2.c3p0:type=PooledDataSource,identityToken=z8kflt9u1r0kgrq1ycbj82|7fd50002,name=z8kflt9u1r0kgrq1ycbj82|7fd50002 registered.
记录日志!!!
20:52:27.380 [main] INFO com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource - Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 2, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 10000, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, dataSourceName -> z8kflt9u1r0kgrq1ycbj82|7fd50002, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, extensions -> {}, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, forceSynchronousCheckins -> false, forceUseNamedDriverClass -> false, identityToken -> z8kflt9u1r0kgrq1ycbj82|7fd50002, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://localhost:3306/springmvc?useUnicode=true&characterEncoding=utf8&useSSL=true, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 30, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 10, numHelperThreads -> 3, preferredTestQuery -> null, privilegeSpawnedThreads -> false, properties -> {user=******, password=******}, propertyCycle -> 0, statementCacheNumDeferredCloseThreads -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, userOverrides -> {}, usesTraditionalReflectiveProxies -> false ]
20:52:27.392 [main] DEBUG com.mchange.v2.cfg.MConfig - The configuration file for resource identifier '/mchange-commons.properties' could not be found. Skipping.
20:52:27.392 [main] DEBUG com.mchange.v2.cfg.MConfig - The configuration file for resource identifier '/mchange-log.properties' could not be found. Skipping.
20:52:27.392 [main] DEBUG com.mchange.v2.cfg.MConfig - The configuration file for resource identifier '/c3p0.properties' could not be found. Skipping.
20:52:27.392 [main] DEBUG com.mchange.v2.cfg.MConfig - The configuration file for resource identifier 'hocon:/reference,/application,/c3p0,/' could not be found. Skipping.
20:52:27.393 [main] WARN com.mchange.v2.resourcepool.BasicResourcePool - Bad pool size config, start 3 < min 10. Using 10 as start.
20:52:27.394 [main] DEBUG com.mchange.v2.resourcepool.BasicResourcePool - com.mchange.v2.resourcepool.BasicResourcePool@2b0f373b config: [start -> 10; min -> 10; max -> 30; inc -> 3; num_acq_attempts -> 2; acq_attempt_delay -> 1000; check_idle_resources_delay -> 0; max_resource_age -> 0; max_idle_time -> 0; excess_max_idle_time -> 0; destroy_unreturned_resc_time -> 0; expiration_enforcement_delay -> 0; break_on_acquisition_failure -> false; debug_store_checkout_exceptions -> false; force_synchronous_checkins -> false]
20:52:27.394 [main] DEBUG com.mchange.v2.c3p0.impl.C3P0PooledConnectionPoolManager - Created new pool for auth, username (masked): 'ro******'.
20:52:27.394 [main] DEBUG com.mchange.v2.resourcepool.BasicResourcePool - acquire test -- pool size: 0; target_pool_size: 10; desired target? 1
20:52:27.394 [main] DEBUG com.mchange.v2.resourcepool.BasicResourcePool - awaitAvailable(): [unknown]
记录成功!!!