Spring IOC/DI 相关注解
- @Configuration 等价于applicationContext.xml配置
@Configuration
public class ApplicationConfig {
//...
}
使用程序实现加载工厂
AnnotationConfigApplicationContext ctx= new AnnotationConfigApplicationContext(ApplicationConfig.class);
老版本编写
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
使用程序实现加载工厂
ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
- @Bean 等价 标签
@Bean(name="date")
public Date getDate() {
return new Date();
}
等价
<bean id="date" class="java.util.Date"</bean>
@Bean属性介绍
属性名 | 类型 | 是否必须 | 说明 |
---|---|---|---|
name | String | 否 | 等价于<bean /> 的id属性,不可以和value同时出现 |
value | String | 否 | 等价于<bean /> 的id属性,不可以和name同时出现 |
autowire | Bean | 否 | 自动注入方式Autowire.NO/BY_NAME/BY_TYPE |
destroyMethod | String | 否 | 等价于<bean /> 的destroy-method属性 |
init-method | String | 否 | 等价于<bean /> 的init-method属性 |
- @Component 、@Service、@Repository、@Controller
以上注解主要面向分层开发时候需要对不同层次的Bean分类管理。
@Service("userService")
public class UserService implements IUserService{
}
- @ComponentScan
该注解用于扫描指定spring容器的bean,并且将扫描的bean放置到Spring工厂中。
@Configuration
@ComponentScan(basePackages="com.baizhi",
excludeFilters= {
@Filter(type=FilterType.ANNOTATION,value=Controller.class)
},
includeFilters= {
@Filter(Service.class),
@Filter(Repository.class)
}
)
public class ApplicationConfig {
//...
}
等价写法
<context:component-scan base-package="com.baizhi">
<context:include-filter
type="annotation"
expression="org.springframework.stereotype.Repository,
org.springframework.stereotype.Service"/>
<context:exclude-filter
type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
- Bean生命周期控制@PreDestroy、@PostConstruct、@Scope
@Component("otherComponent")
@Scope(value=BeanDefinition.SCOPE_PROTOTYPE|SCOPE_SINGLETON)
public class OtherComponent {
public OtherComponent() {
// TODO Auto-generated constructor stub
}
@PostConstruct
public void init() {
System.out.println("init...");
}
@PreDestroy
public void destory() {
System.out.println("destory...");
}
}
默认Spring工厂管理的Bean都是单例的,同时Spring会在工厂初始化的时候就创建配置的组件bean。这个时候如果想在工厂创建或者工厂消亡的时候执行某些方法时候,可以考虑添加@PostConstruct、@PreDestroy注解即可。
等价写法
<bean id="otherComponent" class="com.baizhi.other.OtherComponent"
scope="prototype|singleton"
init-method="init"
destroy-method="destory"/>
- @Lazy
表示延迟spring工厂初始化Bean等价 beans标签的default-lazy-init="true"属性
@Configuration
@Lazy
public class ApplicationConfig {
}
- @Autowired、@Resource、@Inject、@Qualifier、@Primary
@Bean(name="date1")
public Date getDate1() {
return new Date();
}
@Bean(name="date2")
public Date getDate2() {
return new Date();
}
@Bean("date3")
public Date date(@Qualifier("date1")Date date) {
System.out.println("000000000");
return date;
}
@Bean管理的Bean的参数默认是自动注入的,此时如果发现有两个相同类型的Bean出现就会导致注入失败,解决之道是使用@Qualifier注解指定注入的参数当然也可以使用@Primary指定默认参与注入的类型。
@Bean(name="date1")
@Primary
public Date getDate1() {
return new Date();
}
@Bean(name="date2")
public Date getDate2() {
return new Date();
}
@Bean("date3")
public Date date(Date date) {
System.out.println("000000000");
return date;
}
@Autowired和@Resource都是解决Spring的值注入的不同的是@Autowired按照类型注入,此时如果容器存在两个相同类型的Bean组件的时候此时注入则是失败的。这个时候需要和@Qulifier或者@Primary注解联合使用解决类型冲突问题;@Resource注解是JDK1.6主持的注解该注解一旦指定name之后只会按照name注入如果没有指定name属性,先按照name注入如果查找不到再按照类型注入。@Inject和@Autowired用法一致但是使用@Inject需要导入额外的Maven依赖,而且如果存在类型冲突需要使用@Named注解指明需要注入的组件名字
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
- @Required
该属性用于作用在set方法上表明在初始化该Bean的时候,该set个方法的属性必须设置值。一般和配置文件联合使用
public class UserService implements IUserService{
private Date date;
public Date getDate() {
return date;
}
@Required
public void setDate(Date date) {
this.date = date;
}
}
配置文件
<context:annotation-config/>
<bean id="userService" class="com.baizhi.service.impl.UserService">
<property name="date">
<bean class="java.util.Date"></bean>
</property>
</bean>
注意必须配置context:annotation-config/属性,否则@Required不起作用
- @ImportResource(引入xml配置)
@Configuration
@ImportResource(value= {"classpath:applicationContext.xml"})
public class ApplicationConfig {
}
- @Import(引入配置类)
@Configuration
public class ConfigA {
@Bean
public A a() {
return new A();
}
}
@Configuration
@Import(ConfigA.class)
public class ConfigB {
@Bean
public B b() {
return new B();
}
}
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);
// now both beans A and B will be available...
A a = ctx.getBean(A.class);
B b = ctx.getBean(B.class);
}
- @Profile
@Configuration
@Import(value= {DevConfig.class,ProConfig.class})
public class ApplicationConfig {
}
-----------------------
@Configuration
@ImportResource(value= {"classpath:applicationContext.xml"})
@Profile("dev")
public class DevConfig {
}
-----------------------
@Configuration
@Profile("pro")
public class ProConfig {
}
测试环境
AnnotationConfigApplicationContext ctx= new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("pro");
ctx.register(ApplicationConfig.class);
ctx.refresh();
System.out.println(ctx.getBean("userService"));
ctx.close();
- @ImportResource(引入外部配置资源)
@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
public class AppConfig {
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public DataSource dataSource() {
return new DriverManagerDataSource(url, username, password);
}
}
properties-config.xml
<beans>
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
</beans>
jdbc.properties
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=root
@AspectJ相关注解
@AspectJ是一种aspects 的风格,它是用注释注释的常规Java类。
该注解用于开启**@AspactJ**支持
@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages = "com.baizhi")
public class AopConfig {
}
等价写法
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
该注解用于声明切面类
@Aspect
@Component
public class AspectModule {
}
该注解用于声明切入点
@Pointcut("execution(* com.baizhi..*.*(..))") // 切入点表达式
public void pc(){}
常用的切入点标志符
切入点标志符 | 说明 |
---|---|
execution | 匹配执行方法的连接点 |
within | 限定匹配特定类型的连接点 |
this | 用于指定AOP代理必须是指定类型的实例,用于匹配该对象的所有连接点 |
target | 用于限定目标对象必须是指定类型的实例,用于匹配该对象的所有连接点 |
args | 用于对连接点的参数类型进行限制,要求参数的类型时指定类型的实例 |
@annotation | 用于对连接点的注解类型进行限制,要求注解的类型为指定类型的实例 |
该注解用于声明前置通知
@Aspect
public class AspectModule {
@Pointcut("execution(* com.baizhi..*.*(..))")
public void pc(){}
// 参数为切入点方法名(好处,定义一个切入点实现复用)
@Before("pc()")
// JoinPoint 用于获取连接点信息(如获取方法和类信息)
public void before(JoinPoint joinPoint){
System.out.println("-------------before advice-----------");
}
}
老版本写法
public class MyBeforeAdvice implements MethodBeforeAdvice{
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("-------------before advice-----------");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="com.baizhi.service.UserServiceImpl"></bean>
<bean id="beforeAdvice" class="MyBeforeAdvice"></bean>
<aop:config>
<aop:pointcut id="pc" expression="execution(* com.baizhi..*.*(..))"></aop:pointcut>
<aop:advisor advice-ref="beforeAdvice" pointcut-ref="pc"></aop:advisor>
</aop:config>
</beans>
该注解用于声明匹配方法正常返回的后置通知
@AfterReturning("pc()")
public void afterReturning(){
System.out.println("-------------after returning advice-----------");
}
正常返回
非正常返回
该注解用于声明异常通知(目标方法产生异常执行的代码片段)
@AfterThrowing("pc()")
public void afterThrowing(){
System.out.println("-------------after throwing advice-----------");
}
切入方法抛出异常
该注解用于声明后置通知(无论目标方法是否产生异常都会执行的代码片段)
@After("pc()")
public void after(){
System.out.println("-------------after advice-----------");
}
该注解用于声明环绕通知(在调用目标方法前后执行)
@Around("pc()")
// 注意:参数ProceedingJoinPoint只能适用于环绕通知,用于获取连接点信息
public void aroud(ProceedingJoinPoint pjp){
System.out.println("-------------aroud advice start-----------");
try {
// 调用目标方法 并获取目标方法返回值
Object proceed = pjp.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("-------------aroud advice end-----------");
}
该注解用于指定多切面执行顺序(@Order值越小优先级越高)
@Aspect
@Component
@Order(1)
public class AspectModule {
@Pointcut("execution(* com.baizhi..*.*(..))")
public void pc(){}
@Before("pc()")
public void before1(JoinPoint joinPoint){
System.out.println("------before1 advice first execute-----");
}
}
@Aspect
@Component
@Order(5)
public class AspectModule1 {
@Pointcut("execution(* com.baizhi..*.*(..))")
public void pc(){}
@Before("pc()")
public void before2(JoinPoint joinPoint){
System.out.println("------before2 advice second execute-----");
}
}
老版本写法
<aop:config>
<aop:pointcut id="pc" expression="execution(* com.baizhi..*.*(..))"></aop:pointcut>
<aop:advisor advice-ref="beforeAdvice1" pointcut-ref="pc" order="1"></aop:advisor>
<aop:advisor advice-ref="beforeAdvice2" pointcut-ref="pc" order="5"></aop:advisor>
</aop:config>
Spring MVC相关注解
该注解用于开启基于注解的MVC支持
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.baizhi")
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// TODO Auto-generated method stub
registry.addResourceHandler("/**")
.addResourceLocations("/")
.setCachePeriod(1000000);
}
}
老版本写法
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
启动引导类
/**
* @author gaozhy
* @date 2018/4/8.11:18
*/
public class WebInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(MvcConfig.class);
ctx.setServletContext(servletContext);
ServletRegistration.Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
servlet.addMapping("/");
servlet.setLoadOnStartup(1);
}
}
该注解指示特定的类充当控制器的角色
@Controller
public class HelloController {
}
该注解为组合注解@Controller和@ResponseBody(控制器实现REST API,响应JSON、XML或自定义的MediaType内容。无需用@ResponseBody注释所有的@RequestMapping方法。),用于构建Restful风格的控制器类
@RestController
public class RestfulController {
}
@RequestMapping(value = "/",method = RequestMethod.GET)
public String index(){
return "Hello World";
}
以下注解用于简化HTTP方法的映射,并更好地表达带注释的处理程序方法的语义。例如,@GetMapping 等同于@RequestMapping(method = RequestMethod.GET)
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping
该注解放在方法参数前,用于绑定URL中携带的Value
/**
* 例如:http://localhost:8080/owners/1
*/
@GetMapping("/owners/{ownerId}")
@ResponseBody
public String findOwner(@PathVariable String ownerId) {
System.out.println(ownerId);
return ownerId;
}
用法一:应用在普通方法上
被 @ModelAttribute 注解的方法会在Controller每个目标方法执行之前都执行(注:对于一个Controller中包含多个URL的时候,要谨慎使用),并且会将返回值自动存储到Model中(key默认为首字母小写的返回值名,value为方法返回值)
/**
* 例如:http://localhost:8080/owners/1
*/
@GetMapping("/owners/{ownerId}")
@ResponseBody
public String findOwner(@PathVariable String ownerId) {
System.out.println(ownerId);
return ownerId;
}
@ModelAttribute
public User getUser(){
System.out.println("----------进入到 getUser method----------");
return new User(1,"张三");
}
@ModelAttribute
public Person getPerson(){
System.out.println("----------进入到 getPerson method----------");
return new Person(1,"李四","北京");
}
在请求http://localhost:8080/owners/1,会依次访问getUser、getPerson,然后再去访问目标方法findOwner,被@ModelAttribute注解的方法的返回值会自动添加到Model中保存起来。
类似于
@GetMapping("/owners/{ownerId}")
@ResponseBody
public String findOwner(@PathVariable String ownerId,Model model) {
model.addAttribute("user",new User(1,"张三"));
model.addAttribute("person",new Person(1,"李四","北京"));
System.out.println(ownerId);
return ownerId;
}
- 用法二:应用在方法的参数上
将Model中Key对应的Value绑定给指定参数
@ModelAttribute
public User getUser(){
System.out.println("----------进入到 getUser method----------");
return new User(1,"张三");
}
@ModelAttribute
public Person getPerson(){
System.out.println("----------进入到 getPerson method----------");
return new Person(1,"李四","北京");
}
@GetMapping("/test")
@ResponseBody
public String test(@ModelAttribute("user")User user,@ModelAttribute("person")Person person) {
System.out.println(user);
System.out.println(person);
return "test ok";
}
该注解使用在类上,用于将Model中的数据保存到HttpSession中(跨请求传递数据)
属性名 | 说明 |
---|---|
value | 指定存放到Session域中Model的key的键名 |
type | 指定存放到Session域中Model的value的类型 |
@Controller
@SessionAttributes(value={"name"},types = Integer.class)
public class HelloController {
@RequestMapping("/test1")
public String test1(Model model){
System.out.println("--------------test1-----------------");
model.addAttribute("name","zs");
model.addAttribute("age",18);
model.addAttribute("sex","male");
return "redirect:test2";
}
@RequestMapping("/test2")
@ResponseBody
public String test2(ModelMap modelMap, SessionStatus sessionStatus){
System.out.println("--------------test2-----------------");
System.out.println(modelMap.get("name") + " | "+ modelMap.get("age")+ " | "+modelMap.get("sex"));
// 清除Session中保存的数据
sessionStatus.setComplete();
return "test2 ok";
}
}
该注解用于将Session中key对应的value绑定给指定参数
@RequestMapping("/test3")
@ResponseBody
public String test3(@SessionAttribute("name") String name,@SessionAttribute("age") Integer age){
System.out.println("--------------test3-----------------");
System.out.println(name + " | " + age);
return "test3 ok";
}
该注解用于将Request中key对应的value绑定给指定参数
@RequestMapping("/test4")
@ResponseBody
public String test4(@RequestAttribute("sex")String sex){
System.out.println("--------------test4-----------------");
System.out.println(sex);
return "test4 ok";
}
该注解用于绑定请求参数给控制器方法指定参数
// http://localhost:8080/test5?id=1&name=zs
@RequestMapping("/test5")
@ResponseBody
public String test5(@RequestParam("id") Integer id,@RequestParam("name") String username){
System.out.println("--------------test5-----------------");
System.out.println(id + " | " + username);
return "test5 ok";
}
该注解用于将请求头中参数绑定给控制器指定方法参数
@RequestMapping("/test6")
@ResponseBody
public String test6(@RequestHeader Map requestHeaderMap){
System.out.println("--------------test6-----------------");
requestHeaderMap.forEach((k,v) -> System.out.println(k + " | " +v));
return "test6 ok";
}
注:在上面的案列中,请求头中所有数据会以key-value的方式存放到Map集合中,也可以使用 @RequestHeader(“Accept-Encoding”) 这样的方式,获取特定信息。
该注解用于将Cookie中的value绑定给控制器指定方法参数
@RequestMapping("/test7")
@ResponseBody
public String test7(@CookieValue("JSESSIONID") String jessionId){
System.out.println("--------------test7-----------------");
System.out.println(jessionId);
return "test7 ok";
}
该注解用于将请求主体通过HttpMessageConverter读取和反序列化为对象
@PostMapping("/test8")
@ResponseBody
public String test8(@RequestBody User user){
System.out.println(user);
return "test8 ok";
}
该注解用于将方法返回值通过HttpMessageConverter序列化到响应体
该注解用于支持跨越请求处理
@PostMapping("/test10")
@ResponseBody
@CrossOrigin(origins = "http://192.168.31.187:8080",maxAge = 1800)
public String test10(String name,Integer id){
System.out.println(name + " | " + id);
return "test10 ok";
}
浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任一不同,都是跨域