Spring中提供了各种Aware接口,比较常见的。。。
1 简介 Spring中的注解大概可以分为两大类:
spring的bean容器相关的注解,或者说bean工厂相关的注解;
springmvc相关的注解。
spring的bean容器相关的注解,先后有:@Required, @Autowired, @PostConstruct, @PreDestory,还有Spring3.0开始支持的JSR-330标准javax.inject.*中的注解(@Inject, @Named, @Qualifier, @Provider, @Scope, @Singleton).``
springmvc相关的注解有:@Controller, @RequestMapping, @RequestParam, @ResponseBody等等。
要理解Spring中的注解,先要理解Java中的注解。
2 Java中的注解 1 @Override Java中1.5中开始引入注解,我们最熟悉的应该是:@Override, 它的定义如下:
1 2 3 4 @Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override {}
从注释,我们可以看出,@Override的作用是,提示编译器,使用了@Override注解的方法必须override父类或者java.lang.Object中的一个同名方法。我们看到@Override的定义中使用到了 @Target, @Retention,它们就是所谓的“**元注解**”——就是定义注解的注解,
2 @Retention 1 2 3 4 5 6 7 8 9 10 @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { RetentionPolicy value () ; }
@Retention用于提示注解被保留多长时间,有三种取值:
1 2 3 4 5 public enum RetentionPolicy { SOURCE, CLASS, RUNTIME }
3 @Target 1 2 3 4 5 6 7 8 9 10 11 12 @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { ElementType[] value(); }
@Target用于提示该注解使用的地方,取值有:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public enum ElementType { TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE, TYPE_PARAMETER, TYPE_USE }
所以:
1 2 3 4 @Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override {}
表示 @Override 只能使用在方法上,保留在源码级别,被编译器处理,然后抛弃掉。
4 @Documented 1 2 3 4 5 @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Documented {}
表示注解是否能被 javadoc 处理并保留在文档中。
3 自定义注解 有了元注解,那么我就可以使用它来自定义我们需要的注解。结合自定义注解和AOP或者过滤器,是一种十分强大的武器。比如可以使用注解来实现权限的细粒度的控制——在类或者方法上使用权限注解,然后在AOP或者过滤器中进行拦截处理。下面是一个关于登录的权限的注解的实现:
1 2 3 4 5 6 7 8 @Target({ ElementType.METHOD, ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface NoLogin {}
我们自定义了一个注解 @NoLogin, 可以被用于 方法 和 类 上,注解一直保留到运行期,可以被反射读取到。该注解的含义是:被 @NoLogin 注解的类或者方法,即使用户没有登录,也是可以访问的。下面就是对注解进行处理了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 public class CheckLoginInterceptor implements HandlerInterceptor { private static final Logger logger = Logger.getLogger(CheckLoginInterceptor.class); @Override public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (!(handler instanceof HandlerMethod)) { logger.warn("当前操作handler不为HandlerMethod=" + handler.getClass().getName() + ",req=" + request.getQueryString()); return true ; } HandlerMethod handlerMethod = (HandlerMethod) handler; String methodName = handlerMethod.getMethod().getName(); NoLogin noLogin = handlerMethod.getMethod().getAnnotation(NoLogin.class); if (null != noLogin) { if (logger.isDebugEnabled()) { logger.debug("当前操作methodName=" + methodName + "不需要检查登录情况" ); } return true ; } noLogin = handlerMethod.getMethod().getDeclaringClass().getAnnotation(NoLogin.class); if (null != noLogin) { if (logger.isDebugEnabled()) { logger.debug("当前操作methodName=" + methodName + "不需要检查登录情况" ); } return true ; } if (null == request.getSession().getAttribute(CommonConstants.SESSION_KEY_USER)) { logger.warn("当前操作" + methodName + "用户未登录,ip=" + request.getRemoteAddr()); response.getWriter().write(JsonConvertor.convertFailResult(ErrorCodeEnum.NOT_LOGIN).toString()); return false ; } return true ; } @Override public void postHandle (HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion (HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }
上面我们定义了一个登录拦截器,首先使用反射 来判断方法上是否被 @NoLogin 注解:
NoLogin noLogin = handlerMethod.getMethod().getAnnotation(NoLogin.class);
然后判断类是否被 @NoLogin 注解:
noLogin = handlerMethod.getMethod().getDeclaringClass().getAnnotation(NoLogin.class);
如果被注解了,就返回 true,如果没有被注解,就判断是否已经登录,没有登录则返回错误信息给前台和false. 这是一个简单的使用 注解 和 过滤器 来进行权限处理的例子。扩展开来,那么我们就可以使用注解,来表示某方法或者类,只能被具有某种角色,或者具有某种权限的用户所访问,然后在过滤器中进行判断处理。
4 spring常用注解 @Configuration @Bean 1 2 3 4 5 6 7 @Configuration public class MainConfig { @Bean public Person person () { return new Person(); } }
注意: 通过@Bean的形式是使用的话, bean的默认名称是方法名,若@Bean(value=”bean的名称”) 那么bean的名称是指定的
去容器中读取Bean的信息(传入配置类)
1 2 3 4 public static void main ( String[] args ) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class); System.out.println(ctx.getBean("person" )); }
@CompentScan 在配置类上写@CompentScan注解来进行包扫描
1 2 3 4 @Configuration @ComponentScan(basePackages = {"com.tuling.testcompentscan"}) public class MainConfig {}
1:排除用法 excludeFilters(排除@Controller注解的,和TulingService的)
1 2 3 4 5 @Configuration @ComponentScan(basePackages = {"com.tuling.testcompentscan"},excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class}), @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = {TulingService.class}) }) public class MainConfig { }
2:包含用法 includeFilters ,注意,若使用包含的用法,需要把useDefaultFilters属性设置为false(true表 示扫描全部的)
1 2 3 4 5 @Configuration @ComponentScan(basePackages = {"com.tuling.testcompentscan"},includeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class, Service.class}) },useDefaultFilters = false) public class MainConfig {}
@Scope 配置Bean的作用域对象
1:在不指定@Scope的情况下,所有的bean都是单实例的bean,而且是饿汉加载(容器启动实例就创建 好了)
1 2 3 4 @Bean public Person person () { return new Person(); }
2:指定@Scope为 prototype 表示为多实例的,而且还是懒汉模式加载(IOC容器启动的时候,并不会创建对象,是 在第一次使用的时候才会创建)
1 2 3 4 @Bean @Scope(value = "prototype") public Person person () { return new Person(); }
3:@Scope指定的作用域方法取值
a) singleton 单实例的(默认) b) prototype 多实例的 c) request 同一次请求 d) session 同一个会话级别
@Lazy Bean的懒加载@Lazy(主要针对单实例的bean 容器启动的时候,不创建对象,在第一次使用的时候才会创建该象)
1 2 3 4 5 @Bean @Lazy public Person person () { return new Person(); }
@Conditional @Conditional进行条件判断等.
场景,有二个组件TulingAspect 和TulingLog ,我的TulingLog组件是依赖于TulingAspect的组件
应用:自己创建一个TulingCondition的类 实现Condition接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class TulingCondition implements Condition {@Override public boolean matches (ConditionContext context, AnnotatedTypeMetadata metadata) { if (context.getBeanFactory().containsBean("tulingAspect" )) { return true ; } return false ; } } public class MainConfig { @Bean public TulingAspect tulingAspect () { return new TulingAspect(); } @Conditional(value = TulingCondition.class) public TulingLog tulingLog () { return new TulingLog(); } }
往IOC 容器中添加组件 1:通过@CompentScan +@Controller @Service @Respository @compent
适用场景: 针对我们自己写的组件可以通过该方式来进行加载到容器中。
2:通过@Bean的方式来导入组件(适用于导入第三方组件的类)
3:通过@Import来导入组件 (导入组件的id为全类名路径)
1 2 3 4 @Configuration @Import(value = {Person.class, Car.class}) public class MainConfig {}
通过@Import 的ImportSeletor类实现组件的导入 (导入组件的id为全类名路径)
1 2 3 4 5 6 7 8 9 10 11 public class TulingImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{"com.tuling.testimport.compent.Dog" }; } } @Configuration @Import(value = {Person.class, Car.class, TulingImportSelector.class}) public class MainConfig {}
通过@Import的 ImportBeanDefinitionRegister导入组件 (可以指定bean的名称)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class TulingBeanDefinitionRegister implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions (AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Cat.class); registry.registerBeanDefinition("cat" ,rootBeanDefinition); } } @Configuration @Import(value = {Person.class, Car.class, TulingImportSelector.class, TulingBeanDefinitionRegister.class}) public class MainConfig {}
4:通过实现FacotryBean接口来实现注册 组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class CarFactoryBean implements FactoryBean <Car > { @Override public Car getObject () throws Exception { return new Car(); } @Override public Class<?> getObjectType() { return Car.class; } @Override public boolean isSingleton () { return true ; } }
Bean的初始化和销毁 bean的创建—–>初始化—–>销毁方法 由容器管理Bean的生命周期,我们可以通过自己指定bean的初始化方法和bean的销毁方法
1 2 3 4 5 6 7 8 @Configuration public class MainConfig { @Bean(initMethod = "init",destroyMethod = "destroy") public Car car () { return new Car(); } }
针对单实例bean的话,容器启动的时候,bean的对象就创建了,而且容器销毁的时候,也会调用Bean的销毁方法
针对多实例bean的话,容器启动的时候,bean是不会被创建的而是在获取bean的时候被创建,而且bean的销毁不受 IOC容器的管理.
2:通过 InitializingBean和DisposableBean 的二个接口实现bean的初始化以及销毁方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Component public class Person implements InitializingBean ,DisposableBean { public Person () { System.out.println("Person的构造方法" ); } @Override public void destroy () throws Exception { System.out.println("DisposableBean的destroy()方法 " ); } @Override public void afterPropertiesSet () throws Exception { System.out.println("InitializingBean的 afterPropertiesSet方法" ); } }
3:通过JSR250规范 提供的注解@PostConstruct 和@ProDestory标注的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Component public class Book { public Book () { System.out.println("book 的构造方法" ); } @PostConstruct public void init () { System.out.println("book 的PostConstruct标志的方法" ); } @PreDestroy public void destory () { System.out.println("book 的PreDestory标注的方法" ); } }
4:通过Spring的BeanPostProcessor的 bean的后置处理器会拦截所有bean创建过程
postProcessBeforeInitialization 在init方法之前调用 postProcessAfterInitialization 在init方法之后调用
1 2 3 4 5 6 7 8 9 10 11 12 @Component public class TulingBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization (Object bean, String beanName) { System.out.println("postProcessBeforeInitialization:" +beanName); return bean; } @Override public Object postProcessAfterInitialization (Object bean, String beanName) { System.out.println("postProcessAfterInitialization:" +beanName); return bean; } }
BeanPostProcessor的执行时机
1 2 3 4 5 6 7 8 populateBean(beanName, mbd, instanceWrapper) initializeBean{ applyBeanPostProcessorsBeforeInitialization() invokeInitMethods{ isInitializingBean.afterPropertiesSet 自定义的init方法 } applyBeanPostProcessorsAfterInitialization()方法 }
@Value @PropertySource 注入普通字符
1 2 @Value("Michael Jackson") String name;
注入操作系统属性
1 2 @Value("#{systemProperties['os.name']}") String osName;
注入表达式结果
1 2 @Value("#{ T(java.lang.Math).random() * 100}") String randomNumber;
注入其它bean属性
1 2 @Value("#{domeClass.name}") String name;
注入文件资源
1 2 @Value("#{classpath:com/hgs/hello/test.txt}") String Resoutce file;
注入网站资源
1 2 @Value("http://www.cznovel.com") Resource url;
注入配置文件
1 2 @Value("${book.name}") Resource url;
注入配置使用方法:
1 2 @PropertySource 加载配置文件(类上)@PropertySource("classpath:com/hgs/hello/test.properties")
使用示例:
1 2 3 4 5 6 7 8 9 10 public class Person {private String firstName; @Value("#{28-8}") private Integer age; 通过读取外部配置文件的值 @Value("${person.lastName}") private String lastName; }
1 2 3 4 5 6 7 8 @Configuration @PropertySource(value = {"classpath:person.properties"}) @Bean public Person person () { return new Person(); } }
@Profile 通过@Profile注解 来根据环境来激活标识不同的Bean
@Profile标识在类上,那么只有当前环境匹配,整个配置类才会生效 @Profile标识在Bean上 ,那么只有当前环境的Bean才会被激活,没有标志为@Profile的bean 不管在什么环境都可以被激活
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 @Configuration @PropertySource(value = {"classpath:ds.properties"}) public class MainConfig implements EmbeddedValueResolverAware { @Value("${ds.username}") private String userName; @Value("${ds.password}") private String password; private String jdbcUrl; private String classDriver; @Override public void setEmbeddedValueResolver (StringValueResolver resolver) { this .jdbcUrl = resolver.resolveStringValue("${ds.jdbcUrl}" ); this .classDriver = resolver.resolveStringValue("${ds.classDriver}" ); } @Profile(value = "test") public DataSource testDs () { return buliderDataSource(new DruidDataSource()); } @Profile(value = "dev") public DataSource devDs () { return buliderDataSource(new DruidDataSource()); } @Profile(value = "prod") public DataSource prodDs () { return buliderDataSource(new DruidDataSource()); } private DataSource buliderDataSource (DruidDataSource dataSource) { dataSource.setUsername(userName); dataSource.setPassword(password); dataSource.setDriverClassName(classDriver); dataSource.setUrl(jdbcUrl); return dataSource; } }
激活切换环境的方法
方法一:通过运行时jvm参数来切换 -Dspring.profiles.active=test|dev|prod 方法二:通过代码的方式来激活
1 2 3 4 5 public static void main (String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.getEnvironment().setActiveProfiles("test" ,"dev" ); ctx.register(MainConfig.class); ctx.refresh(); printBeanName(ctx); }
4 切面AOP相关注解 @Aspect 声明一个切面(类上)
使用@After、@Before、@Around定义建言(advice),可直接将拦截规则(切点)作为参数。
@After 在方法执行之后执行(方法上)
@Before 在方法执行之前执行(方法上)
@Around 在方法执行之前与之后执行(方法上)
@PointCut 声明切点
在java配置类中使用@EnableAspectJAutoProxy注解开启Spring对AspectJ代理的支持(类上)
@Enable*注解说明 这些注解主要用来开启对xxx的支持。
@EnableAspectJAutoProxy 开启对AspectJ自动代理的支持
@EnableAsync 开启异步方法的支持
@EnableScheduling 开启计划任务的支持
@EnableWebMvc 开启Web MVC的配置支持
@EnableConfigurationProperties 开启对@ConfigurationProperties注解配置Bean的支持
@EnableJpaRepositories 开启对SpringData JPA Repository的支持
@EnableTransactionManagement 开启注解式事务的支持
@EnableTransactionManagement 开启注解式事务的支持
@EnableCaching 开启注解式的缓存支持
11 测试相关注解 @RunWith 运行器,Spring中通常用于对JUnit的支持
1 @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration 用来加载配置ApplicationContext,其中classes属性用来加载配置类
1 @ContextConfiguration(classes={TestConfig.class})
5 SpringMVC常用注解 @EnableWebMvc 在配置类中开启Web MVC的配置支持,如一些ViewResolver或者MessageConverter等,若无此句,重写WebMvcConfigurerAdapter方法(用于对SpringMVC的配置)。
@Controller 声明该类为SpringMVC中的Controller
@RequestMapping 用于映射Web请求,包括访问路径和参数(类或方法上)
@ResponseBody 支持将返回值放在response内,而不是一个页面,通常用户返回json数据(返回值旁或方法上)
@RequestBody 允许request的参数在request体中,而不是在直接连接在地址后面。(放在参数前)
@PathVariable 用于接收路径参数,比如@RequestMapping(“/hello/{name}”)申明的路径,将注解放在参数中前,即可获取该值,通常作为Restful的接口实现方法。
@RestController 该注解为一个组合注解,相当于@Controller和@ResponseBody的组合,注解在类上,意味着,该Controller的所有方法都默认加上了@ResponseBody。
@ControllerAdvice 通过该注解,我们可以将对于控制器的全局配置放置在同一个位置,注解了@Controller的类的方法可使用@ExceptionHandler、@InitBinder、@ModelAttribute注解到方法上,
这对所有注解了 @RequestMapping的控制器内的方法有效。
@ExceptionHandler 用于全局处理控制器里的异常
@InitBinder 用来设置WebDataBinder,WebDataBinder用来自动绑定前台请求参数到Model中。
@ModelAttribute 本来的作用是绑定键值对到Model里,在@ControllerAdvice中是让全局的@RequestMapping都能获得在此处设置的键值对。
参考链接