1. 增强类型扩展
(1) 异常抛出增强
异常抛出增强(Throws Advice)可以在方法抛出异常时执行特定的逻辑,例如日志记录或事务回滚。这种增强类型使得我们能够对异常进行统一处理,提升代码的可维护性和可读性。
package cn.ktjy.aop;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
public class ErrorLogger {
private static final Logger logger=Logger.getLogger(ErrorLogger.class);
public void afterThrowing(JoinPoint jp,RuntimeException e){
logger.error(jp.getSignature().getName()+"方法发生异常:"+e);
}
}
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* service.UserService.*(..))"/>
<aop:aspect ref="theLogger">
<aop:after-throwing method="afterThrowing"
pointcut-ref="pointcut" throwing="e"/>
</aop:aspect>
</aop:config>
(2) 最终增强
最终增强(After Finally Advice)无论目标方法是否抛出异常,都会在方法执行后运行。这类增强通常用于资源的清理操作,如关闭文件流或数据库连接。
package cn.ktjy.aop;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
public class AfterLogger {
private static final Logger logger=Logger.getLogger(AfterLogger.class);
public void afterLogger(JoinPoint jp){
logger.info(jp.getSignature().getName()+"方法结束执行。");
}
}
<bean id="theLogger" class="cn.ktjy.aop.AfterLogger"></bean>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* service.UserService.*(..))"/>
<aop:aspect ref="theLogger">
<aop:after method="afterLogger" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
(3) 环绕增强
环绕增强(Around Advice)提供了在方法执行前后执行自定义逻辑的能力。这种增强类型最为灵活,可以在方法调用前后添加行为,甚至可以完全替代目标方法的执行。
package cn.ktjy.aop;
import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import java.util.Arrays;
public class AroundLogger {
private static final Logger logger=Logger.getLogger(AroundLogger.class);
public Object aroundLogger(ProceedingJoinPoint jp)throws Throwable{
logger.info("调用"+jp.getTarget()+"的"+jp.getSignature().getName()+"方法。方法入参:"+ Arrays.toString(jp.getArgs()));
try {
Object result=jp.proceed();
logger.info("调用"+jp.getTarget()+"的"+jp.getSignature().getName()+"方法。方法返回值:"+result);
return result;
} catch (Throwable e) {
logger.error(jp.getSignature().getName()+"方法发生异常:"+e);
throw e;
} finally {
logger.info(jp.getSignature().getName()+" 方法结束执行。 ");
}
}
}
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* .UserService.*(..))"/>
<aop:aspect ref="theLogger">
<aop:around method="aroundLogger" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
2. 依赖注入方式扩展
(1) 构造注入
构造注入通过构造器为类注入依赖项。它确保了对象在创建时就已经具备所有必需的依赖,适用于强制依赖的场景。
package cn.ktjy.service.impl;
import cn.ktjy.dao.UserDao;
import cn.ktjy.dao.impl.UserDaoImpl;
import cn.ktjy.pojo.User;
import cn.ktjy.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
public class UserServiceImpl implements UserService {
// //实例化所依赖的UserDao对象
// private UserDao dao=new UserDaoImpl();
//
// /**
// * 保存用户信息
// * @param user
// */
// public void save(User user) {
// dao.save(user);
// }
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
private UserDao userDao;
public int save(User user){
return userDao.save(user);
}
}
<bean id="userDao" class="dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="service.impl.UserServiceImpl">
<constructor-arg name="userDao" ref="userDao"/>
</bean>
(3) p命名空间注入
p命名空间注入是一种简化的属性注入方式,使用XML配置文件中的p命名空间来注入属性,减少了配置文件的复杂度。
注意:在beans标签中加入xmlns:p="http://www.springframework.org/schema/p",否则无法使用p命名空间
<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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="LuFei" class="pojo.Speak" p:name="路飞" p:speak="我是要成为海贼王的男人!"/>
<bean id="KongMing" class="pojo.Speak" p:name="诸葛亮" p:speak="从未见过如此厚颜无耻之人!"/>
</beans>
(4) 不同数据类型的注入
Spring支持多种数据类型的注入,包括基本数据类型、集合类型、Map类型等。了解并熟练掌握这些注入方式,有助于我们在实际项目中灵活应用。
<bean id="user" class="cn.ktjy.pojo.User">
<property name="username">
<value>诸葛亮</value>
</property>
<property name="age">
<value>27</value>
</property>
<property name="evaluate">
<value>鞠躬尽瘁,死而后已</value>
</property>
</bean>
<bean id="entity" class="cn.ktjy.pojo.TestEntity">
<property name="emptyValue">
<value></value>
</property>
<property name="nullValue">
<null/>
</property>
</bean>
<bean id="entity" class="cn.ktjy.pojo.TestEntity">
<property name="specialCharacter1">
<value><![CDATA[诸葛亮&路飞]]></value>
</property>
<property name="specialCharacter2">
<value>诸葛亮&路飞</value>
</property>
</bean>
<bean id="entity" class="cn.ktjy.pojo.TestEntity">
<property name="list">
<list>
<value>青龙偃月刀</value>
<value>丈八点钢矛</value>
</list>
</property>
</bean>
<bean id="entity" class="cn.ktjy.pojo.TestEntity">
<property name="set">
<set>
<value>青龙偃月刀</value>
<value>丈八点钢矛</value>
</set>
</property>
</bean>
<bean id="entity" class="cn.ktjy.pojo.TestEntity">
<property name="map">
<map>
<entry>
<key><value>knife</value></key>
<value>青龙偃月刀</value>
</entry>
<entry>
<key><value>spear</value></key>
<value>丈八点钢矛</value>
</entry>
</map>
</property>
</bean>
<bean id="entity" class="cn.ktjy.pojo.TestEntity">
<property name="props">
<props>
<prop key="knife">青龙偃月刀</prop>
<prop key="spear">丈八点钢矛</prop>
</props>
</property>
</bean>
<bean id="user" class="cn.ktjy.pojo.User"/>
<bean id="entity" class="cn.ktjy.pojo.TestEntity">
<property name="outUser">
<ref bean="user"/>
</property>
</bean>
<bean id="entity" class="cn.ktjy.pojo.TestEntity">
<property name="innerBean">
<bean class="cn.ktjy.pojo.User"/>
</property>
</bean>
3. 使用注解方式完成Bean的装配
通过Spring的注解机制,可以简化配置和代码,使用注解如@Service
、@Component
等标注业务类,使用@AutoWire、@Resource实现用户保存功能。
@Autowired
- 来源:
@Autowired
是Spring框架提供的注解。 - 自动注入: 默认情况下,
@Autowired
根据类型进行自动注入。它会在Spring容器中查找匹配类型的bean,并自动注入到标注的属性、构造器或方法中。 - 可选属性:
required
属性:默认为true
,表示如果Spring容器中没有找到匹配的bean,会抛出异常。如果设置为false
,则在没有匹配bean时,不会注入。 - 使用场景: 适用于需要按照类型进行注入的场景,特别是在Spring框架的上下文中使用频繁。
@Resource
- 来源:
@Resource
是Java标准的注解(由JSR-250规范定义),并由Spring支持。 - 名称优先:
@Resource
默认情况下根据名称进行注入。它首先会查找与属性名或指定名称匹配的bean。如果找不到匹配的名称,再根据类型查找。 - 属性:
name
属性:指定要注入的bean的名称。如果没有指定name
属性,则使用标注的属性名。type
属性:指定要注入的bean的类型。
- 使用场景: 适用于需要按照名称进行注入的场景,特别是在需要与Java EE兼容的环境中使用较多。
package cn.ktjy.dao.impl;
import cn.ktjy.dao.UserDao;
import cn.ktjy.pojo.User;
import org.springframework.stereotype.Component;
@Component("userDao")
public class UserDaoImpl implements UserDao {
// //通过工厂获取所依赖的UserDao对象
// private UserDao dao= UserDaoFactory.getInstance();
/**
* 保存用户信息
* @param user
*/
public int save(User user) {
int count=1;
System.out.println("保存用户到数据库");
// throw new RuntimeException("测试异常");
return count;
}
}
package cn.ktjy.service.impl;
import cn.ktjy.dao.UserDao;
import cn.ktjy.dao.impl.UserDaoImpl;
import cn.ktjy.pojo.User;
import cn.ktjy.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service("userService")
public class UserServiceImpl implements UserService {
// //实例化所依赖的UserDao对象
// private UserDao dao=new UserDaoImpl();
//
// /**
// * 保存用户信息
// * @param user
// */
// public void save(User user) {
// dao.save(user);
// }
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
@Autowired
private UserDao userDao;
public int save(User user){
return userDao.save(user);
}
}
4. 使用注解实现Spring AOP
(1) 使用注解方式标注切面
通过注解如@Aspect
和@Pointcut
等,标注和定义切面,使AOP的实现更加简洁和直观。
package cn.ktjy.aop;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import java.util.Arrays;
@Aspect
public class UserServiceLogger {
private static final Logger log=Logger.getLogger(UserServiceLogger.class);
/**
* 前置增强
* @param jp
*/
public void before(JoinPoint jp){
log.info("调用 "+jp.getTarget()+"的"+jp.getSignature().getName()+"方法。方法入参:"+ Arrays.toString(jp.getArgs()));
}
/**
* 后置增强
* @param jp
* @param returnValue
*/
public void afterReturning(JoinPoint jp,Object returnValue){
log.info("调用"+jp.getTarget()+"的"+jp.getSignature().getName()+"方法,方法返回值:"+returnValue);
}
}
(2) 使用注解定义其他类型的增强
使用注解如@Before
、@After
、@Around
等,可以方便地定义各种类型的增强逻辑,满足不同的业务需求。
package cn.ktjy.aop;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import java.util.Arrays;
@Aspect
public class UserServiceLogger {
private static final Logger log=Logger.getLogger(UserServiceLogger.class);
/**
* 前置增强
* @param jp
*/
@Before("execution(* cn.ktjy.service.UserService.*(..))")
public void before(JoinPoint jp){
log.info("调用 "+jp.getTarget()+"的"+jp.getSignature().getName()+"方法。方法入参:"+ Arrays.toString(jp.getArgs()));
}
/**
* 后置增强
* @param jp
* @param returnValue
*/
@AfterReturning(pointcut = "execution(* cn.ktjy.service.UserService.*(..))",returning = "returnValue")
public void afterReturning(JoinPoint jp,Object returnValue){
log.info("调用"+jp.getTarget()+"的"+jp.getSignature().getName()+"方法,方法返回值:"+returnValue);
}
}
根据不同的应用场景选择合适的依赖注入(DI)和面向切面编程(AOP)方式,可以提高代码的可维护性、可扩展性和模块化程度。