这是我参与8月更文挑战的第21天,活动详情查看:8月更文挑战
| 作者:江夏
| CSDN:blog.csdn.net/qq_41153943
| 知乎:www.zhihu.com/people/1024…
| GitHub:github.com/JiangXia-10…
本文大概10368字,读完共需32分钟
废话部分
注解和 xml 都是一种元数据,元数据即解释数据的数据,就是所谓配置。在传统的Spring开发中是使用.xml文件来对bean进行注入或者是配置aop、事物,但是这么做有两个缺点:
1、如果所有的内容都配置在.xml文件中,那么.xml文件将会十分庞大;如果按需求分开.xml文件,那么.xml文件又会非常多。所以这种做法导致配置文件的可读性与可维护性变得很低。
2、在开发中在.java文件和.xml文件之间不断切换,是一件麻烦的事,同时这种思维上的不连贯也会降低开发的效率。 问题都是用来解决的,所以为了解决.xml配配置文件带来的问题,Spring引入了注解,通过"@XXX"的方式,让注解与Java Bean紧密结合,注解方式减少了配置文件内容,更加便于管理,并且使用注解可以大大提高了开发效率!
本文按照分类讲解Spring中常用的一些注解。
正文部分
1、声明bean的注解
1、@Component:泛指各种组件,就是说当我们的类不属于各种归类的时候(不属于@Controller、@Services等的时候),我们就可以使用@Component来标注这个类,把普通pojo实例化到spring容器中,相当于配置文件中的:。所以可以理解为@Component可以细化为@Reposity,@Service,@Controller。
@Component("conversionImpl")
//其实默认的spring中的Bean id 为 conversionImpl(首字母小写)
public class ConversionImpl implements Conversion {
@Autowired
private RedisClient redisClient;
}
2、@Service 在业务逻辑层使用(service层注入dao)用于标注服务层,主要用来进行业务的逻辑处理
@Service()
public class UserService{
@Resource
private UserDao userDao;
public void add(){
userDao.add();
}
}
3、@Repository:用于标注数据库访问层,也可以说被作为持久层操作(数据库)的bean来使用,即dao层。
@Repository(value="userDao")注解是告诉Spring,让Spring创建一个名字叫“userDao”的UserDaoImpl实例。当Service需要使用Spring创建的名字叫“userDao”的UserDaoImpl实例时,就可以使用@Resource(name = "userDao")注解告诉Spring,Spring把创建好的userDao注入给Service即可。
如果在DaoImpl中加了@Repository,那么在spring的扫包机制下,也会生成这个dao的bean,注入serviceImpl中的:
//也可以使用@Component,效果都是一样的,只是为了声明为bean
@Repository
@Mapper
public interface UserDao {
@Insert("insert into user(account, password, user_name) " +
"values(#{user.account}, #{user.password}, #{user.name})")
int insertUser(@Param("user") User user) throws RuntimeException;
}
@Autowired
private UserDao UserDao;
4、@Controller 在展现层使用,控制器的声明。用于标记在一个类上,使用它标记的类就是一个SpringMvc Controller对象,分发处理器会扫描使用该注解的类的方法,并检测该方法是否使用了@RequestMapping注解。
@Controller只是定义了一个控制器类,而使用@RequestMapping注解的方法才是处理请求的处理器。
@Controller标记在一个类上还不能真正意义上说它就是SpringMvc的控制器,因为这个时候Spring还不认识它,这个时候需要把这个控制器交给Spring来管理。
@Controller
public class HelloWorld{
@RequestMapping(value="/hello")
public String hello() {
return "hello";
}
@Autowried
private IocSerevce service;
public void add(){
service.add();
}
}
2、注入bean的注解
1、@Autowired:在Spring 2.5 引入了 @Autowired 注释,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。 通过 @Autowired的使用来消除 set ,get方法。在使用@Autowired之前,xml文件对一个bean配置起属性时,是这用的:
<property name="属性名" value=" 属性值"/>
定义了一个UserRepository接口,其中定义了一个save方法:
public interface UserRepository {
void save();
}
再定义一个UserRepository接口的实现类,并实现save方法,在这里指定了该bean在IoC中标识符名称为userRepository。
@Repository("userRepository")
public class UserRepositoryImps implements UserRepository{
@Override
public void save() {
System.out.println("UserRepositoryImps save");
}
}
需要一个UserRepository类型的属性,通过@Autowired自动装配方式,从IoC容器中去查找到,并返回给该属性:
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public void save(){
userRepository.save();
}
}
2、@Inject:@Inject是JSR330 (Dependency Injection for Java)中的规范,需要导入javax.inject.Inject;实现注入。@Inject可以作用在变量、setter方法、构造函数上。是根据类型进行自动装配的,如果需要按名称进行装配,则需要配合@Named。
将@Inject可以作用在变量、setter方法、构造函数上,和使用@Autowired一样:
public class User{
private Person person;
@Inject
pbulic void setPerson(Person person){
this.person = person;
}
}
3、@Named("XXX") 中的 XXX是 Bean 的名称,所以 @Inject和 @Named结合使用时,自动注入的策略就从 byType 转变成 byName 了。
public class User{
private Person person;
@Inject
pbulic void setPerson(@Named("main")Person person)
{
this.person = person;
}
}
4、@Resource:@Resource是JSR250规范的实现,需要导入javax.annotation实现注入。@Resource可以作用在变量、setter方法上。@Resource是根据名称进行自动装配的,一般会指定一个name属性。
public class User{
private Person person;
@Resource(name="myPerson")
pbulic void setPerson(Person person){
this.person = person;
}
}
@Autowired、@Inject和@Resource都可以注解在set方法和属性上,推荐注解在属性上。
3、配置类相关注解
1、@Bean:Spring的@Bean注解用于告诉方法,产生一个Bean对象,然后这个Bean对象交给Spring管理。产生这个Bean对象的方法Spring只会调用一次,随后这个Spring将会将这个Bean对象放在自己的IOC容器中。SpringIOC 容器管理一个或者多个bean,这些bean都需要在@Configuration注解下进行创建,在一个方法上使用@Bean注解就表明这个方法需要交给Spring进行管理。
@Configuration
public class AppConfig {
// 未指定bean 的名称,默认采用的是 "方法名" + "首字母小写"的配置方式
@Bean
public MyBean myBean(){
return new MyBean();
}
}
public class MyBean {
public MyBean(){
System.out.println("MyBean Initializing");
}
}
@Bean 注解的属性有:value、name、autowire、initMethod、destroyMethod。
name 和 value 两个属性是相同的含义的, 在代码中定义了别名。为 bean 起一个名字,如果默认没有写该属性,那么就使用方法的名称为该 bean 的名称。
autowire指定 bean 的装配方式, 根据名称 和 根据类型 装配, 一般不设置,采用默认即可。autowire指定的装配方式 有三种Autowire.NO (默认设置)、Autowire.BY_NAME、Autowire.BY_TYPE。
initMethod和destroyMethod指定bean的初始化方法和销毁方法, 直接指定方法名称即可,不用带括号。
public class MyBean {
public MyBean(){
System.out.println("MyBean Initializing");
}
public void init(){
System.out.println("Bean 初始化方法被调用");
}
public void destroy(){
System.out.println("Bean 销毁方法被调用");
}
}
@Configuration
public class AppConfig {
// @Bean
@Bean(initMethod = "init", destroyMethod = "destroy")
public MyBean myBean(){
return new MyBean();
}
}
2、@Scope:@Scope 设置Spring容器如何新建Bean实例,默认是@scope("Singleton "),即单例模式。其设置类型包括:
Singleton (单例,一个Spring容器中只有一个bean实例,默认模式),
Protetype (每次调用新建一个bean),
Request (web项目中,给每个http request新建一个bean),
Session (web项目中,给每个http session新建一个bean),
GlobalSession(给每一个 global http session新建一个Bean实例)
@Configuration
public class myConfig {
//默认是单例的。不需要特别说明
@Bean("person")
public Person person(){
return new Person("binghe002", 18);
}
}
@Configuration
public class myConfig {
//Person对象的作用域修改成prototype,多例的
@Scope("prototype")
@Bean("person")
public Person person(){
return new Person("binghe002", 18);
}
}
3、@Configuration :从Spring3.0,@Configuration用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。
//定义该类为配置类
@Configuration
public class myConfiguration {
public myConfiguration() {
System.out.println("myConfiguration容器启动初始化。。。");
}
}
4、@ComponentScan :@ComponentScan主要就是定义扫描的路径从中找出标识了需要装配的类自动装配到spring的bean容器中。前面说到过@Controller注解,@Service,@Repository注解,它们其实都是组件,属于@Component注解,而@ComponentScan注解默认就会装配标识了@Controller,@Service,@Repository,@Component注解的类到spring容器中。
//扫描com.demo下的组件
@ComponentScan(value="com.demo")
@Configuration
public class myConfig {
}
5、@WishlyConfiguration 为@Configuration与@ComponentScan的组合注解,可以替代这两个注解。
4、@Value注解
1、@Value :@Value的作用是通过注解将常量、配置文件中的值、其他bean的属性值注入到变量中,作为变量的初始值。 支持如下方式的注入: (1)、普通注入
@Value("张三")
private String name; // 注入普通字符串
(2)、bean属性、系统属性、表达式注入,使用@Value("#{}")。bean属性注入需要注入者和被注入者属于同一个IOC容器,或者父子IOC容器关系,在同一个作用域内。
// 注入其他Bean属性:注入beanInject对象的属性another,类具体定义见下面
@Value("#{beanInject.another}")
private String fromAnotherBean;
// 注入操作系统属性
@Value("#{systemProperties['os.name']}")
private String systemPropertiesName;
//注入表达式结果
@Value("#{T(java.lang.Math).random() * 100.0 }")
private double randomNumber;
(3)、配置文件属性注入@Value("${}")
@Value("#{}")读取配置文件中的值,注入到变量中去。配置文件分为默认配置文件application.properties和自定义配置文件
•application.properties。application.properties在spring boot启动时默认加载此文件
•自定义属性文件。自定义属性文件通过@PropertySource加载。@PropertySource可以同时加载多个文件,也可以加载单个文件。如果相同第一个属性文件和第二属性文件存在相同key,则最后一个属性文件里的key启作用。加载文件的路径也可以配置变量,如下文的${anotherfile.configinject},此值定义在第一个属性文件config.properties
第一个属性文件config.properties内容如下:
${anotherfile.configinject}作为第二个属性文件加载路径的变量值
book.name=bookName
anotherfile.configinject=placeholder
第二个属性文件config_placeholder.properties内容如下:
book.name.placeholder=bookNamePlaceholder
@Component
// 引入自定义配置文件。
@PropertySource({"classpath:com/hry/spring/configinject/config.properties",
// 引入自定义配置文件。${anotherfile.configinject}则是config.properties文件中的第二个属性值,会被替换为config_placeholder.properties。
"classpath:com/hry/spring/configinject/config_${anotherfile.configinject}.properties"})
public class ConfigurationFileInject{
@Value("${app.name}")
private String appName; // 这里的值来自application.properties,spring boot启动时默认加载此文件
@Value("${book.name}")
private String bookName; // 注入第一个配置文件config.properties的第一个属性
@Value("${book.name.placeholder}")
private String bookNamePlaceholder; // 注入第二个配置外部文件属性
}
5、切面(AOP)相关注解
在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程,简称AOP(aspect object programming)。AOP编程,可以将一些系统性相关的编程工作,独立提取出来,独立实现,然后通过切面切入进系统。从而避免了在业务逻辑的代码中混入很多的系统相关的逻辑——比如权限管理,事物管理,日志记录等等。这些系统性的编程工作都可以独立编码实现,然后通过AOP技术切入进系统即可。从而达到了 将不同的关注点分离出来的效果。
aop技术的功能是让关注点与业务逻辑代码进行分离;而重复的代码就是关注点;关注点形成的类,就是切面(类)
Spring支持AspectJ的注解式aop编程,需要在java的配置类中使用@EnableAspectJAutoProxy注解开启Spring对AspectJ代理的支持。下面介绍下aop编程的相关注解。
先说说@EnableAspectJAutoProxy注解,看看它的源码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
/**
* Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
* to standard Java interface-based proxies. The default is {@code false}.
*/
boolean proxyTargetClass() default false;
/**
* Indicate that the proxy should be exposed by the AOP framework as a {@code ThreadLocal}
* for retrieval via the {@link org.springframework.aop.framework.AopContext} class.
* Off by default, i.e. no guarantees that {@code AopContext} access will work.
* @since 4.3.1
*/
boolean exposeProxy() default false;
}
这里有两个方法,一个是控制aop的具体实现方式,为true 的话使用cglib,为false的话使用java的Proxy,默认为false,第二个参数控制代理的暴露方式,解决内部调用不能使用代理的场景,默认为false。
@Aspect:声明一个切面(类)上,作用是把当前类标识为一个切面供容器读取。
在切面类中需要定义切面方法用于响应响应的目标方法,切面方法即为通知方法,通知方法需要用注解标识,AspectJ 支持 5 种类型的通知注解:
@Before: 前置通知, 在方法执行之前执行
@After: 后置通知, 在方法执行之后执行 。
@AfterRunning: 返回通知, 在方法返回结果之后执行
@AfterThrowing: 异常通知, 在方法抛出异常之后
@Around: 环绕通知, 围绕着方法执行
@PointCut :声明切点,是植入Advice(通知)的触发条件。每个Pointcut的定义包括2部分,一是表达式,二是方法签名。方法签名必须是 public及void型。可以将Pointcut中的方法看作是一个被Advice引用的助记符,因为表达式不直观,因此我们可以通过方法签名的方式为 此表达式命名。因此Pointcut中的方法只需要方法签名,而不需要在方法体内编写实际代码。
/**
* 日志切面
*/
@Component
@Aspect
public class LoggingAspect {
/**
* 前置通知:目标方法执行之前执行以下方法体的内容
*/
@Before("execution(* com.qcc.beans.aop.*.*(..))")
public void beforeMethod(JoinPoint jp){
String methodName = jp.getSignature().getName();
System.out.println("【前置通知】the method 【" + methodName + "】 begins with " + Arrays.asList(jp.getArgs()));
}
/**
* 返回通知:目标方法正常执行完毕时执行以下代码
*/
@AfterReturning(value="execution(* com.qcc.beans.aop.*.*(..))",returning="result")
public void afterReturningMethod(JoinPoint jp, Object result){
String methodName = jp.getSignature().getName();
System.out.println("【返回通知】the method 【" + methodName + "】 ends with 【" + result + "】");
}
/**
* 后置通知:目标方法执行之后执行以下方法体的内容,不管是否发生异常。
* @param jp
*/
@After("execution(* com.qcc.beans.aop.*.*(..))")
public void afterMethod(JoinPoint jp){
System.out.println("【后置通知】this is a afterMethod advice...");
}
/**
* 异常通知:目标方法发生异常的时候执行以下代码
*/
@AfterThrowing(value="execution(* com.qcc.beans.aop.*.*(..))",throwing="e")
public void afterThorwingMethod(JoinPoint jp, NullPointerException e){
String methodName = jp.getSignature().getName();
System.out.println("【异常通知】the method 【" + methodName + "】 occurs exception: " + e);
}
总结
以上就是对spring中进行注解开发过程中使用到的一些注解的整理和介绍,spring中的注解还有很多,后续会继续更新其他的注解的一些使用,之前有文章也对这里说到的部分注解进行了详细的单篇文章的介绍,有兴趣的可以移步。
注解单篇详细介绍文章:
1、Spring注解(一):@Configuration、@Bean给容器中注册组件
2、Spring注解(二):@ComponentScan自动扫描组件