基于注解的方式实现
1.配置注解
注解名称 | 说明 |
---|---|
@Configuration | 把一个类作为一个IoC容器,它的某个方法头上如果注册了@Bean,就会作为这个Spring容器中的Bean。 |
@ComponentScan | 在配置类上添加 @ComponentScan 注解。该注解默认会扫描该类所在的包下所有的配置类,相当于之前的 context:component-scan |
@Scope | 用于指定scope作用域的(用在类上) |
@Lazy | 表示延迟初始化 |
@Conditional | Spring4开始提供,它的作用是按照一定的条件进行判断,满足条件给容器注册Bean。 |
@Profile | 本质上就是一个条件注解的实现,它可以通过不同环境返回不同的实例对象,常用于数据源切换等。 |
@Import | 导入外部资源 |
生命周期控制 | @PostConstruct用于指定初始化方法(用在方法上) @PreDestory用于指定销毁方法(用在方法上) @DependsOn:定义Bean初始化及销毁时的顺序 |
1.1 @Configuration
把一个类作为一个IoC容器,它的某个方法头上如果注册了@Bean,就会作为这个Spring容器中的Bean。
@Configuration
public class JavaConfig {
}
1.2 @ComponentScan
package com.gupaoedu;
import com.gupaoedu.controller.UserController;
import com.gupaoedu.dao.IUserDao;
import com.gupaoedu.dao.impl.UserDaoImpl;
import com.gupaoedu.service.IUserService;
import com.gupaoedu.service.impl.UserServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScans;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Controller;
/**
* @ComponentScan 如果不去指定扫描的路径,默认是会扫描当前目录及其子目录下的所有的
* 被@Componenet @Controller @Service @Repository标注的类型
*/
@Configuration
/*@ComponentScan(value = {"com.gupaoedu.controller"}
,useDefaultFilters = false
,includeFilters = {@ComponentScan.Filter(Controller.class)})*/
@ComponentScans({
@ComponentScan(value = {"com.gupaoedu.controller"}
,useDefaultFilters = false
,includeFilters = {@ComponentScan.Filter(Controller.class)})
,@ComponentScan(value = {"com.gupaoedu.service","com.gupaoedu.dao"}
,useDefaultFilters = true
,excludeFilters = {@ComponentScan.Filter(Controller.class)})
})
public class JavaConfig {
}
1.3 @Scope
作用域 | 说明 |
---|---|
prototype | 每次请求,都是一个新的Bean( java原型模式) |
singleton | bean是单例的(Java单例模式) |
request | 在一次请求中,bean的声明周期和request同步 |
session | bean的生命周期和session同步 |
默认的情况是 singleton
1.4 @Lazy
Spring中Bean的作用域默认是单例的,单例对象默认在IoC容器创建之后就进行创建并放入到IoC容器中。使用@Lazy注解则可以让单例对象在真正用到时才进行创建。
1.5 @Conditional
@Conditional注解 通过重写matches方法来动态实现是否加载某类型
package com.gupaoedu.conditional;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class ConditionalOnBean implements Condition {
/**
*
* @param conditionContext
* @param annotatedTypeMetadata
* @return
* true 表示IoC容器加载该类型
* false 表示IoC容器不加载该类型
*/
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
return true;
}
}
package com.gupaoedu;
import com.gupaoedu.conditional.ConditionalOnBean;
import com.gupaoedu.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
@Configuration
public class JavaConfig {
/**
* @Conditional(ConditionalOnBean.class)
* 是一个条件注解 表示如果ConditionalOnBean中的matches方法返回true就加载
* 返回false就不加载
* @return
*/
@Bean
@Conditional(ConditionalOnBean.class)
public User user(){
return new User();
}
public static void main(String[] args) {
ApplicationContext ac = new AnnotationConfigApplicationContext(JavaConfig.class);
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
for (String beanName:beanDefinitionNames){
System.out.println(beanName);
}
}
}
Spring中已实现的Conditional注解的类
@Conditional扩展注解 | 作用(判断是否满足当前指定条件) |
---|---|
@ConditionalOnJava | 系统的Java版本是否符合要全 |
@ConditionalOnBean | 容器中存在指定的Bean |
@ConditionalOnMissingBean | 容器中不存在指定的Bean |
@ConditionalOnExpression | 满足SpEL表达式 |
@ConditionalOnClass | 系统中有指定的类 |
@ConditionalOnMissingClass | 系统中没有指定的类 |
@ConditionalOnSingleCandidate | 容器中只有一个指定的Bean,或者这个Bean是首选Bean |
@ConditionalOnProperty | 系统中指定的属性是否有指定的值 |
@ConditionalOnResource | 类路径下是否存在指定的资源文件 |
@ConditionalOnWebApplication | 当前是Web环境 |
@ConditionalOnNotWebApplication | 当前不是Web环境 |
@ConditionalOnJndi | JNDI存在指定项 |
1.6 @Profile
根据不同的环境返回不同的对象
package com.gupaoedu.pojo;
@Data
public class GpDataSource {
private String username;
private String password;
private String url;
public GpDataSource(String username, String password, String url) {
this.username = username;
this.password = password;
this.url = url;
}
@Override
public String toString() {
return "GpDataSource{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", url='" + url + '\'' +
'}';
}
public GpDataSource() {
}
}
package com.gupaoedu;
import com.gupaoedu.conditional.ConditionalOnBean;
import com.gupaoedu.conditional.ConditionalOnClass;
import com.gupaoedu.pojo.GpDataSource;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.*;
@Configuration
public class JavaConfig {
@Bean
@Profile("pro") // 其实Profile注解本质上就是Conditional的一种实现
public GpDataSource proDataSource(){
GpDataSource ds = new GpDataSource("root","123","192.168.11.190");
return ds;
}
@Bean
@Profile("dev")
public GpDataSource devDataSource(){
GpDataSource ds = new GpDataSource("admin","456","192.168.12.190");
return ds;
}
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
// 通过设置不同环境返回不同对象
ac.getEnvironment().setActiveProfiles("dev");
ac.register(JavaConfig.class);
ac.refresh();
System.out.println(ac.getBean(GpDataSource.class));
}
}
1.7 @Import
将某个类导入到IoC容器中
1.7.1 静态导入
1.7.2 动态导入 - ImportSelector
public class GpImportSelector implements ImportSelector {
/**
*
* @param annotationMetadata
* @return
* IoC 要加载的类型的全路径的字符串数组
*/
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// 在此处实现不同的业务逻辑控制
return new String[]{LoggerService.class.getName(),CacheService.class.getName()};
}
}
1.7.3 动态导入 - ImportBeanDefinitionRegistrar
package com.gupaoedu.demo;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
public class GpImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
*
* @param annotationMetadata
* @param beanDefinitionRegistry IoC容器中管理对象的一个注册器
*/
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
// 需要将添加的对象包装为一个RootBeanDefinition对象
RootBeanDefinition cache = new RootBeanDefinition(CacheService.class);
beanDefinitionRegistry.registerBeanDefinition("cache",cache);
RootBeanDefinition logger = new RootBeanDefinition(LoggerService.class);
beanDefinitionRegistry.registerBeanDefinition("logger",logger);
}
}
1.8 @PostConstruct @PreDestory @DependsOn
@PostConstruct 用于指定初始化方法(用在方法上)
@PreDestory 用于指定销毁方法(用在方法上)
@DependsOn 指定实例化对象的先后顺序
@Component
// Person的实例化依赖于User对象的实例化,也就是User先于Person实例化
@DependsOn({"user"})
public class Person {
public Person(){
System.out.println("Person 构造方法执行了...");
}
}
2.赋值注解
注解名称 | 说明 |
---|---|
@Component | 泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。 |
@Service | 用于标注业务层组件 |
@Controller | 用于标注控制层组件 |
@Repository | 用于标注数据访问组件,即DAO组件。 |
@Value | 普通数据类型赋值 |
@Autowired | 默认按类型装配,如果我们想使用按名称装配,可以结合@Qualifier注解一起使用 |
@PropertySource | 读取配置文件赋值 |
@Qualifier | 如存在多个实例配合使用 |
@Primary | 自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否则将抛出异常 |
@Resource | 默认按名称装配,当找不到与名称匹配的bean才会按类型装配。 |
2.1 @Component @Controller @Service @Repository
@Component:泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注;
@Controller:用于标注控制层组件;
@Service:用于标注业务层组件;
@Repository:用于标注数据访问组件,即DAO组件。
2.2 @Value
package com.gupaoedu.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
@Component
@Data
public class User {
@Value("bobo") // 注入普通的字符串
private String userName ;
@Value("#{systemProperties['os.name']}")
private String systemPropertiesName; // 注入操作系统的信息
@Value("#{T(java.lang.Math).random()*100}")
private double randomNumber; // 注入表达式的结果
@Value("#{person.personName}")
private String fromPersonName; // 注入其他Bean的属性
@Value("classpath:test.txt")
private Resource resourceFile;
@Value("http://www.baidu.com")
private Resource baiduFile;
}
2.3 @Autowired
默认按类型装配,如果我们想使用按名称装配,可以结合@Qualifier注解一起使用
2.4 @Qualifier
如存在多个实例配合使用
2.5 @PropertySource
在Java配置类中通过@PropertySouce注解来显示的引入属性文件
@Configuration
@ComponentScan("com.gupaoedu")
// 显示的指定要加载的属性文件
@PropertySource({"classpath:spring-db.properties"})
public class JavaConfig {
}
2.6 @Primary
自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否则将抛出异常
2.7 @Resource
默认按名称装配,当找不到与名称匹配的bean才会按类型装配。
3. 切面注解
注解名称 | 说明 |
---|---|
@EnableTransactionManagement | 添加对事物管理的支持 |
@Transactional | 配置声明式事物 |
@EnableAspectJAutoProxy | 启用对AspectJ的支持 |
@Aspect | 声明为切面类 |
通知类型 | @Before:前置通知,目标方法执行之前调用。 @AfterReturning:后置通知,目标方法执行完成之后调用。 @Around:环绕通知,目标方法执行前后都会调用方法,且能增强结果。 @AfterThrowing:异常通知,目标方法出现异常调用。 @After:最终通知,无论程序执行是否正常,该通知都会执行。类似于try…catch中finally代码块。 |
3.1 @EnableTransactionManagement
3.2 @Transactional
3.3 @EnableAspectJAutoProxy
@Configuration
@EnableAspectJAutoProxy // 放开AspectJ的使用
@ComponentScan
public class JavaConfig {
public static void main(String[] args) {
ApplicationContext ac = new AnnotationConfigApplicationContext(JavaConfig.class);
IUserService bean = ac.getBean(IUserService.class);
bean.fun1();
}
}
3.4 @Aspect
package com.gupaoedu.aspect;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect // 显示的表明当前类是一个切面类
@Component // 将该对象加载到IoC容器中
public class MyAspectJ01 {
/**
* 要增强目标对象的方法
* 指定目标对象
* 切入点表达式
*/
@Before("execution(* com.gupaoedu.service.impl.*.fun2(..))")
public void aspectMethod01(){
System.out.println("before ....");
}
}
3.5 通知类型
package com.gupaoedu.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class MyAspectJ02 {
/**
* 前置通知
*/
@Before("execution(* com.gupaoedu.service.impl.*.*(..))")
public void before(){
System.out.println("before ...");
}
/**
* 后置通知 获取返回结果
* @param res
*/
@AfterReturning(value = "within(com.gupaoedu.service.impl.*)",returning = "res")
public void afterReturning(Object res){
System.out.println("后置通知..." + res);
}
/**
* 环绕通知
* @param proceedingJoinPoint
* @return
* @throws Throwable
*/
@Around(value = "within(com.gupaoedu.service.impl.*)")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("目标对象before....");
Object[] args = proceedingJoinPoint.getArgs();
Object res = proceedingJoinPoint.proceed(); // 目标对象方法执行
System.out.println("目标对象after...." + res);
return res;
}
/**
* 异常通知
* @param ex
*/
@AfterThrowing(value = "within(com.gupaoedu.service.impl.*)",throwing = "ex")
public void afterThrowing(Exception ex){
System.out.println("异常通知产生了..." + ex);
}
/**
* 最终通知
*/
@After(value = "within(com.gupaoedu.service.impl.*)")
public void after(){
System.out.println("最终通知...");
}
}
3.5.1 切入点表达式
表达式类型 | 说明 |
---|---|
execution | 定位到目标对象的方法上 |
within | 定位到具体的类型上 |
this | 代理对象的类型 |
target | 目标对象的类型 |
args | 参数的类型 |
@args | 传入的参数有被该注解修饰 |
@within | 类型修饰的注解 |
@annotation | 方法修饰的注解 |
execution表达式
语法: execution([访问权限类型] 返回值类型 [全限定类名] 方法名(参数名) [抛出的异常类型])
符合 | 含有 |
---|---|
* | 0到多个符合 |
… | 方法参数中表示任意个参数,用在报名后表示当前包及其子包 |
+ | 用在类名后表示当前类及其子类,用在接口后表接口及其实现 |
示例:
execution(public * *(. .))
指定切入点为:任意公共方法。
execution(* set *(. .))
指定切入点为:任何一个以“set”开始的方法。
execution(* com.xyz.service.*.*(. .))
指定切入点为:定义在service包里的任意类的任意方法。
execution(* com.xyz.service. .*.*(. .))
指定切入点为:定义在service包或者子包里的任意类的任意方法。“..”出现在类名中时,
后面必须跟“*”,表示包、子包下的所有类。
execution(* *.service.*.*(. .))
指定只有一级包下的serivce子包下所有类(接口)中的所有方法为切入点
execution(* *. .service.*.*(. .))
指定所有包下的serivce子包下所有类(接口)中的所有方法为切入点