Spring一些注解功能
1、@Autowired
@Autowired(required=true):表示自动注入时必须能成功注入该对象,否则报错。默认是true@Autowired(required=false):表示自动注入时该对象不是必须注入对象,有就注入,没有也不会报错
2、定制错误页面的json数据
没有自适应效果时应该转发到/error进行自适应响应效果处理
3、@Conditional
@Conditional是Spring4新提供的注解,作用是按照一定条件判断,满足条件才给容器中注册这个bean
4、@Import(重点是两个Import接口的使用)
@Import 参数value接收一个Class数组,将你传入的类以全类名作为id加入IOC容器中
1)、ImportSelector
ImportSelector强调的是复用性,使用它需要创建一个类实现ImportSelector接口,实现方法的返回值是字符串数组,也就是需要注入容器中的组件的全类名。id同样是全类名。
//自定义逻辑返回需要导入的组件
public class MyImportSelector implements ImportSelector {
// 返回值就是导入到容器中的组件全类名
// AnnotationMetadata:当前标注@Import注解的类的所有注解信息
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[] { "test.spring.ZhangSan", "test.spring.LiSi", "test.spring.WangWu" };
}
}
2)、ImportBeanDefinitionRegistrar
spring官方就是用这种方式,实现了@Component、@@Service等注解的动态注入机制。定义了一个ImportBeanDefinitionRegistrar的实现类,然后在有@Configuration注解的配置类上使用@Import导入
具体使用:创建一个ImportBeanDefinitionRegistrar的实现类,实现registerBeanDefinitions方法,对组件进行注入。
public class MyBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* @param annotationMetadata 当前标注@Import注解的类的所有注解信息
* @param beanDefinitionRegistry BeanDefinition的注册类
*/
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(Cat.class);
beanDefinitionRegistry.registerBeanDefinition("cat",beanDefinition);
}
}
//结果如下
@Configuration
@Import(value = {MyBeanDefinitionRegistrar.class})
public class MyConfig {
}
/**测试结果
beanName: org.springframework.context.annotation.internalConfigurationAnnotationProcessor
beanName: org.springframework.context.annotation.internalAutowiredAnnotationProcessor
beanName: org.springframework.context.annotation.internalCommonAnnotationProcessor
beanName: org.springframework.context.event.internalEventListenerProcessor
beanName: org.springframework.context.event.internalEventListenerFactory
beanName: myConfig
beanName: cat
*/
5、@Options
在MyBatis的mapper中使用@Insert注解插入数据信息,但是并没有返回自增主键。如果需要返回自增主键则需要在@Insert后加上@Options注解
@Options(userGeneratedKeys = true, keyProperty = "id")
@Insert("insert into department(departmentName) values(#{departmentName})")
public int insertDept(Department department);
6、Java回调机制
所谓回调:就是A类中调用B类中的某个方法C,然后B类中反过来调用A类中的方法D,D这个方法就叫回调方法。
软件模块之间总是存在着一定的接口,从调用方式上,可以把它们分为三类:同步调用、回调和异步调用。
回调是一种特殊的调用,至于三种方式也有些不同:
- 同步回调:即阻塞,单向的。
- 回调:即双向(类似自行车的两个齿轮)。
- 异步回调:即通过异步消息进行通知。
7、@RequestParam
@RequestParam是将请求参数和控制器方法的形参创建映射关系
@RequestParam注解一共有三个属性:
value:指定为形参赋值的请求参数的参数名
required:设置是否必须传输此请求参数,默认值是true
若设置为true时,则当前请求必须传输value所指定的请求参数,若没有传输该请求参数,且没有设置defaultValue属性,则页面报错400:Required String parameter 'xxx' is not present;若设置为false,则当前请求不是必须传输value所指定的请求参数,若没有传输,则注解所标识的形参的值为null
defaultValue:不管required属性值为true或false,当value所指定的请求参数没有传输或传输的值为""时,则使用默认值为形参赋值。
@Autowired标注在方法上,Spring容器创建当前对象,就会调用方法,完成赋值;方法使用的参数,自定义类型的值从ioc容器中获取。(类似setter方法,参数是什么类型通过自动注入给对象中的属性赋值)
8、@EnableConfigurationProperties
@EnableConfigurationProperties
注解的作用是:使使用@ConfigurationProperties
注解的类生效。
说明:
- 如果一个配置类只配置
@ConfigurationProperties
注解,而没有使用@Component
,那么在IOC容器中是获取不到properties配置文件转化的bean。说白了@EnableConfigurationProperties
相当于把使用@ConfigurationProperties
的类进行了一次注入。 - 测试发现
@ConfigurationProperties
与@EnableConfigurationProperties
关系特别大
测试证明:
@ConfigurationProperties
与@EnableConfigurationProperties的关系
@EnableConfigurationProperties
文档中解释:当@EnableConfigurationProperties
注解应用到你的@Configuration
类时,任何被@ConfigurationProperties
注解的beans将自动被Environment属性配置。这种风格的配置特别适合与SpringApplication的外部YAML配置进行配合使用。
测试发现:
- 使用
@EnableConfigurationProperties
进行注册
@ConfigurationProperties(prefix = "service.properties")
public class HelloServiceProperties{
private static final String SERVICE_NAME = "test-service";
private String msg = SERVICE_NAME;
}
-----------------------------------------------------
@Configuration
@EnableConfigurationProperties(HelloServiceProperties.class)
@ConditionalOnClass(HelloService.class)
@ConditionalOnProperty(prefix = "hello",value = "enable",matchIfMissing = false)
public class HelloServiceAutoConfiguration{
}
-----------------------------------------------------
@RestController
public class ConfigurationPropertiesController{
@Autowired
private HelloServiceProperties helloServiceProperties;
@RequestMapping("/getObjectProperties")
public Object getObjectProperties(){
System.out.println(helloServiceProperties.getMsg());
return myConfigTest.getProperties();
}
}
自动配置设置
service.properties.name=my-test-name
service.properties.ip=192.168.1.1
service.user=keyle
service.port=8080
一切正常,但是HelloServiceAutoConfiguration头部如果不使用@EnableConfigurationProperties
,测试访问会报错。
- 不使用
@EnableConfigurationProperties
进行注册,使用@Component
注册
@ConfigurationProperties(prefix = "service.properties")
@Component
public class HelloServiceProperties{
private static final String SERVICE_NAME = "test-service";
private String msg = SERVICE_NAME;
public String getMsg(){
return msg;
}
public void setMsg(String msg){
this.msg = msg;
}
}
Controller不变,一切正常,如果注释掉@Component
则启动报错
由此证明:两种方式都是将被@ConfigurationProperties
修饰的类,加载到Spring容器中
9. @Param
作用:
用注解来简化xml配置的时候(比如MyBatis的Mapper.xml中的sql参数引入),@Param注解的作用是给参数命名,参数命名后就能根据名字得到参数值,正确的将参数传入sql语句中(一般通过#{ }的方式,${ }会有sql注入问题)。
实例说明:
-
使用@Param注解
Mapper接口方法:
public int getUserDetail(@Param("userid") int userid);
对应Sql Mapper.xml文件:
<select id="getUserDetail" statememtType="CALLABLE" resultMap="baseMap">
Exec WebApi_Get_CustomerList #{userid}
</select>
说明:
当你使用了@Param注解来声明参数时,如果使用#{ }或${ }的方式都可以,当你不使用@Param注解来声明参数时,必须使用#{ }方式,如果使用${ }的方式,会报错。
-
不使用@Param注解
不使用@Param注解时,最好传递Javabean。在SQL语句里就可以直接引用Javabean的属性,而且只能引用Javabean存在的属性。
Mapper接口的方法:
public int getUserDetail(User user);
对应Sql Mapper.xml文件:
<!-- 这里直接引用对象属性即可,不需要对象.属性的方式 -->
<select id="getUserDetail" statementType="CALLABLE" resultMap="baseMap">
Exec WebApi_Get_CustomerList #{userid}
</select>
10. @Transactional
@Transactional可以说是spring中最常用的注解之一了,通常情况下我们在需要对一个service方法添加事务时,加上这个注解,如果发送unchecked exception,就会发生rollback,最典型的例子如下。
@Service
public class StudentService {
@Autowired
StudentDao studentDao;
@Transactional
public void innerSave(int i) {
Student student = new Student();
student.setName("test" + i);
studentDao.save(student);
// i=5 会出现异常
int a = 1 / (i - 5);
}
}
在调用innerSave(5)时会发生运算异常,导致保存操作回滚,不在此赘述了。
新的需求:循环保存10个学生,发生异常时要求回滚。
我们理所当然的写出了下面的代码,在StudentService.java添加如下方法:
public void outerLooper1() {
for(int i = 1; i <= 10; i++) {
try {
innerSave(i);
} catch (Exception e) {
e.printStackTrace();
}
}
}
先考虑一下test5这个学生有没有保存呢?
结果:
依然出现了,考虑下问题出在哪儿呢?
其实也好理解,spring中@Transactional的事务开启,是基于接口或者类的代理被创建的。所以在同一个类中一个普通方法outerLooper1( )调用另一个有事务的方法innerSave( ),事务是不会起作用的。要解决这个问题,一般我的做法是写一个帮助类,注入到当前类中,来完成事务操作。
@Autowired
UtilService utilService;
public void outerLooper2() {
for(int i = 1; i <= 10; i++) {
utilService.innerSave(i);
}
}
在Spring中使用事务需要遵守一些规范和了解一些坑点,别想当然。列举一下一些注意点。
在需要事务管理的地方加@Transactional注解可以被应用于接口定义和接口方法、类定义和类的public方法上。
@Transactional注解只能应用到public可见度的方法上。如果你在protected、private或者package-visible的方法上使用@Transactional注解,它也不会报错,但是这个被注解的方法将不会展示已配置的事务设置。
Spring团队建议在具体的类(或类的方法)上使用@Transactional注解,而不要使用在类所要实现的任何接口上。在接口上使用@Transactional注解,只能当你设置了基于接口的代理时它才生效。因为注解是不能被继承的,这就意味着如果正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装。
@Transactional的事务开启,或者是基于接口的或者是基于类的代理被创建。所以在同一个类中一个方法调用另一个有事务的方法,事务是不会起作用的。
了解事务的隔离级别,各个数据库默认的隔离级别是不一样的,在spring中用的是isolation = Isolation.READ_COMMITTED来设置。
了解事务的传播机制,当发生事务嵌套时,按照业务选择对应的传播机制,用propagation = Propagation.REQUIRED来设置。
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术