一、Spring的增强类型
1、前置增强
前置增强(Before Advice)是AOP中的一种增强类型,它指在目标方法执行之前执行的增强逻辑。前置增强可以用于在目标方法执行之前做一些准备工作、参数校验、权限检查等操作。
在Spring框架中,可以通过切面(Aspect)和拦截器(Interceptor)来实现前置增强。下面是前置增强的一些特点:
-
在目标方法执行前执行:前置增强的逻辑会在目标方法执行之前被执行,可以在这个时候对目标方法的参数进行检查或做一些准备工作。
-
可以修改参数或返回值:前置增强可以通过修改目标方法的参数或返回值来影响目标方法的执行结果。
-
可以中断目标方法的执行:如果在前置增强中发现某些条件不满足,可以选择中断目标方法的执行,从而避免不必要的操作或错误的结果。
-
可以与其他增强类型组合使用:前置增强可以与其他增强类型(如后置增强、环绕增强)结合使用,实现更复杂的功能和逻辑。
示例代码如下所示,演示了一个使用注解实现前置增强的示例:
@Aspect
public class MyAspect {
@Before("execution(* com.cskt.spring_demo2.service..*.*(..)))")
public void before(){
System.out.println("前置通知.... 在方法之前要執行的公共代码放在这里");
}
}
以上代码使用了Spring的注解和切面来定义前置增强。在@Before注解中指定了目标方法的执行表达式,然后在增强方法中编写了前置增强的逻辑。
通过使用前置增强,可以在目标方法执行之前执行一些额外的逻辑,实现对目标方法的增强和控制
2、后置增强
后置增强(After Advice)是AOP中的一种增强类型,它指在目标方法执行之后执行的增强逻辑。后置增强可以用于在目标方法执行之后做一些清理工作、日志记录、异常处理等操作。
在Spring框架中,可以通过切面(Aspect)和拦截器(Interceptor)来实现后置增强。下面是后置增强的一些特点:
-
在目标方法执行后执行:后置增强的逻辑会在目标方法执行之后被执行,可以在这个时候进行一些清理工作或记录执行结果。
-
可以获取目标方法的返回值:后置增强可以获取目标方法的返回值,并对其进行处理或记录。
-
可以处理目标方法的异常:后置增强可以捕获目标方法抛出的异常,并进行相应的处理,如记录日志或进行异常转换。
-
可以与其他增强类型组合使用:后置增强可以与其他增强类型(如前置增强、环绕增强)结合使用,实现更复杂的功能和逻辑。
示例代码如下所示,演示了一个使用注解实现后置增强的示例:
@Aspect
public class MyAspect {
@AfterReturning(value="execution(* com.cskt.spring_demo2.service..*.*(..)))",returning = "returnVal")
public void AfterReturning(Object returnVal){
System.out.println("后置通知...."+returnVal);
}
}
3、异常抛出增强
异常抛出增强(After Throwing Advice)是AOP中的一种增强类型,它指在目标方法抛出异常后执行的增强逻辑。异常抛出增强可以用于捕获和处理目标方法抛出的异常。
在Spring框架中,可以通过切面(Aspect)和注解(如@AfterThrowing)来实现异常抛出增强。下面是异常抛出增强的一些特点:
-
在目标方法抛出异常后执行:异常抛出增强的逻辑会在目标方法抛出异常之后被执行,可以在这个时候进行异常处理、错误日志记录等操作。
-
可以捕获目标方法抛出的异常:异常抛出增强可以捕获目标方法抛出的异常,并进行相应的处理。可以通过方法参数来获取抛出的异常对象。
-
可以选择性地处理异常:异常抛出增强可以根据不同的异常类型来选择性地处理异常。可以使用注解中的参数来指定要处理的异常类型。
-
可以与其他增强类型组合使用:异常抛出增强可以与其他增强类型(如前置增强、后置增强)结合使用,实现更复杂的功能和逻辑。
示例代码如下所示,演示了一个使用注解实现异常抛出增强的示例:
/**
* 抛出异常通知
* @param e
*/
@AfterThrowing(value="execution(* com.cskt.spring_demo2.service..*.*(..))",throwing = "e")
public void afterThrowable(Throwable e){
System.out.println("出现异常:msg="+e.getMessage());
}
4、最终增强
最终增强(After Finally Advice)是AOP中的一种增强类型,它指在目标方法执行完毕(不管是否抛出异常)后执行的增强逻辑。最终增强可以用于执行一些必须在目标方法执行结束后执行的清理工作。
在Spring框架中,可以通过切面(Aspect)和注解(如@After)来实现最终增强。下面是最终增强的一些特点:
-
在目标方法执行完毕后执行:最终增强的逻辑会在目标方法执行完毕后被执行,无论目标方法是否抛出异常。
-
可以执行清理工作:最终增强可以用于执行一些必须在目标方法执行结束后执行的清理工作,如资源释放、事务回滚等操作。
-
不影响目标方法的执行结果:最终增强不会影响目标方法的执行结果,无论目标方法是否抛出异常。
-
可以与其他增强类型组合使用:最终增强可以与其他增强类型(如前置增强、后置增强、异常抛出增强)结合使用,实现更复杂的功能和逻辑。
示例代码如下所示,演示了一个使用注解实现最终增强的示例:
/**
* 无论什么情况下都会执行的方法
*/
@After(value="execution(* com.cskt.spring_demo2.service..*.*(..))")
public void after(){
//方法的对象的关闭
//方法的回滚
System.out.println("最终通知....");
}
5、环绕增强
环绕增强(Around Advice)是AOP中的一种增强类型,它指在目标方法执行前后完全控制目标方法的增强逻辑。环绕增强可以在目标方法执行前后自定义处理逻辑,并且可以决定是否继续执行目标方法。
在Spring框架中,可以通过切面(Aspect)和注解(如@Around)来实现环绕增强。下面是环绕增强的一些特点:
-
完全控制目标方法的执行:环绕增强可以在目标方法执行前后自定义处理逻辑,并且可以决定是否继续执行目标方法。
-
可以修改目标方法的参数和返回值:通过环绕增强,可以修改目标方法的参数值和返回值,并对目标方法的执行结果进行处理。
-
可以处理异常:环绕增强可以捕获目标方法抛出的异常,并进行相应的处理。
-
可以与其他增强类型组合使用:环绕增强可以与其他增强类型(如前置增强、后置增强、异常抛出增强)结合使用,实现更复杂的功能和逻辑。
示例代码如下所示,演示了一个使用注解实现环绕增强的示例:
/**
* 环绕通知
* @param joinPoint 可用于执行切点的类
* 具体什么业务
* @return
* @throws Throwable
*/
@Around("execution(* com.cskt.spring_demo2.service..*.*(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕通知前....");
Object obj= (Object) joinPoint.proceed();
System.out.println("环绕通知后....");
return obj;
}
二、Spring实现依赖注入的方式
依赖注入的方式:
-
构造方法注入(Constructor Injection):通过类的构造方法来注入依赖对象。在类的构造方法中声明依赖对象的参数,并在创建对象时将依赖对象作为参数传入。
-
Setter方法注入(Setter Injection):通过类的Setter方法来注入依赖对象。在类中定义Setter方法,并在配置文件或使用注解中指定依赖对象的值。
-
接口注入(Interface Injection):通过接口来注入依赖对象。在类中定义接口,并在配置文件或使用注解中指定实现该接口的类。
-
注解注入(Annotation Injection):通过注解来注入依赖对象。使用Spring提供的注解(如@Autowired、@Resource、@Inject等)在类的字段、构造方法或Setter方法上标注依赖对象。
-
方法注入(Method Injection):通过方法来注入依赖对象。在类中定义方法,并在配置文件或使用注解中指定该方法的返回值作为依赖对象。
-
静态工厂方法注入(Static Factory Method Injection):通过静态工厂方法来注入依赖对象。在工厂类中定义静态方法,返回依赖对象,并在配置文件或使用注解中指定使用该静态工厂方法创建对象。
常见的依赖注入方式包括:
1、构造方法注入
以下是一个使用构造方法注入的示例:
public class UserService {
private UserRepository userRepository;
// 构造方法注入依赖对象
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
// 使用依赖对象的方法
public void createUser(User user) {
userRepository.save(user);
}
}
在上面的示例中,UserService类通过构造方法注入了一个UserRepository对象作为依赖。构造方法接收一个UserRepository类型的参数,并将其赋值给类的成员变量userRepository。
通过构造方法注入依赖对象,可以保证UserService在被创建时就有了所需的依赖对象,从而避免了空指针异常等问题。这种方式也使得UserService的依赖关系更加明确,更易于测试和维护。
在使用该示例时,可以通过创建UserRepository对象并传递给UserService的构造方法来实现依赖注入:
UserRepository userRepository = new UserRepositoryImpl();
UserService userService = new UserService(userRepository);
通过这种方式,UserService就可以使用UserRepository对象进行相关操作,而不需要在UserService内部创建或获取UserRepository对象。
2、Setter方法注入
以下是一个使用Setter方法注入的示例:
public class UserService {
private UserRepository userRepository;
// Setter方法注入依赖对象
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
// 使用依赖对象的方法
public void createUser(User user) {
userRepository.save(user);
}
}
在上面的示例中,UserService类通过Setter方法注入了一个UserRepository对象作为依赖。通过定义名为setUserRepository的Setter方法,该方法接收一个UserRepository类型的参数,并将其赋值给类的成员变量userRepository。
通过Setter方法注入依赖对象,可以在UserService对象创建后,通过调用setUserRepository方法来设置依赖对象。这种方式允许在运行时动态地更改依赖对象,增加了灵活性。
在使用该示例时,可以通过创建UserRepository对象并调用UserService的setUserRepository方法来实现依赖注入:
UserRepository userRepository = new UserRepositoryImpl();
UserService userService = new UserService();
userService.setUserRepository(userRepository);
通过这种方式,UserService就可以使用UserRepository对象进行相关操作,而不需要在UserService内部创建或获取UserRepository对象
3、接口注入
以下是一个使用接口注入的示例:
public interface UserRepository {
void save(User user);
}
public class UserRepositoryImpl implements UserRepository {
public void save(User user) {
// 实现保存用户的逻辑
}
}
public class UserService {
private UserRepository userRepository;
// 接口注入依赖对象
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
// 使用依赖对象的方法
public void createUser(User user) {
userRepository.save(user);
}
}
在上面的示例中,定义了一个UserRepository接口,UserService类通过接口注入了一个UserRepository对象作为依赖。通过定义名为setUserRepository的方法,该方法接收一个UserRepository类型的参数,并将其赋值给类的成员变量userRepository。
通过接口注入依赖对象,可以将具体的实现类与UserService解耦,提高代码的灵活性和可维护性。通过将UserRepository接口作为依赖的类型,可以轻松地替换不同的实现类,以满足不同的需求。
在使用该示例时,可以创建UserRepositoryImpl对象,并调用UserService的setUserRepository方法来实现依赖注入:
UserRepository userRepository = new UserRepositoryImpl();
UserService userService = new UserService();
userService.setUserRepository(userRepository);
通过这种方式,UserService就可以使用UserRepository对象进行相关操作,而不需要在UserService内部创建或获取UserRepository对象。同时,如果有其他实现了UserRepository接口的类,也可以轻松地替换掉原有的实现类,实现功能的扩展和替换
4、注解注入
以下是一个使用注解注入的示例:
public interface UserRepository {
void save(User user);
}
@Component
public class UserRepositoryImpl implements UserRepository {
public void save(User user) {
// 实现保存用户的逻辑
}
}
@Service
public class UserService {
private UserRepository userRepository;
// 注解注入依赖对象
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
// 使用依赖对象的方法
public void createUser(User user) {
userRepository.save(user);
}
}
在上面的示例中,定义了一个UserRepository接口和UserRepositoryImpl实现类。UserService类通过注解注入了一个UserRepository对象作为依赖。使用@Autowired注解标注在setUserRepository方法上,表示自动注入UserRepository的实例。
注解方式将Bean的定义信息和Bean实现类结合在一起,Spring提供的注解有
@Component:实现Bean组件的定义
@Repository:用于标注DAO类
@Service:用于标注业务类
@Controller:用于标注控制器类
通过注解注入依赖对象,可以省略手动设置和调用Setter方法的步骤,Spring容器会自动查找匹配的依赖对象进行注入。
在使用该示例时,只需要在Spring配置文件中启用注解扫描,并将相应的类纳入扫描范围:
<context:component-scan base-package="com.example" />
然后,可以使用ApplicationContext或通过注解注入的方式获取UserService对象:
@Autowired
private UserService userService;
通过这种方式,UserService就可以使用UserRepository对象进行相关操作,而不需要在UserService内部创建或获取UserRepository对象。同时,如果有其他实现了UserRepository接口的类,只需要在对应的类上加上@Component注解,Spring会自动注入对应的实例
p命名空间注入
使用p命名空间注入是一种简化的依赖注入方式,在Spring配置文件中可以直接使用p命名空间来注入属性值。
以下是一个使用p命名空间注入的示例:
<beans xmlns="http://www.springframework.org/schema/beans"
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">
<bean id="userService" class="com.example.UserService">
<property name="userRepository" ref="userRepository" />
</bean>
<bean id="userRepository" class="com.example.UserRepositoryImpl" p:username="admin" p:password="123456" />
</beans>
在上面的示例中,通过p命名空间直接在配置文件中指定了UserRepositoryImpl的属性值。通过p:username和p:password可以设置UserRepositoryImpl的username和password属性。
然后,在UserService的配置中使用property元素注入UserRepository实例。
通过这种方式,可以简化依赖注入的配置,直接在配置文件中指定属性值,而不需要在代码中手动设置或通过Setter方法注入属性。
需要注意的是,使用p命名空间注入需要确保Spring配置文件中已声明了p命名空间,并且对应的xsd文件已正确引入。例如,示例中的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"
。
使用p命名空间注入可以简化配置文件的书写,但同时也会增加对Spring命名空间的依赖,需要注意命名空间的正确声明和xsd文件的引入