废话部分
注解和 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中的注解还有很多,后续会继续更新其他的注解的一些使用,之前有文章也对这里说到的部分注解进行了详细的单篇文章的介绍,有兴趣的可以移步。