classA{ AInterface a; A(){} //一个方法 AMethod() { a = new AInterfaceImp(); } } // 当AInterface有一个新的实现但愿使用,那么须要改变classA的代码 // 构造器注入 classA{ AInterface a; A(AInterface a){ this.a = a; } } // setter注入 classA{ AInterface a; A(){} public void setA(AInterface a){ this.a = a; } }
Core模块提供了框架的基本部分,包括IoC和依赖注入功能。java
SpEL模块提供了一种强大的表达式语言,用于在运行时查询和操做对象图。web
Spring容器是Spring Framework的核心。 容器将建立对象,将它们链接在一块儿,配置它们,并管理从建立到销毁的整个生命周期。 Spring容器使用DI来管理组成应用程序的组件。spring
容器经过读取提供的配置元数据来获取有关要实例化,配置和组装的对象的指令。 配置元数据能够由XML,Java注释或Java代码表示。Spring IoC容器利用Java POJO类和配置元数据来生成彻底配置和可执行的系统或应用程序。数据库
Spring提供两种不一样的容器编程
No | 容器和描述 |
---|---|
1 | 这是提供DI基本支持的最简单容器,由org.springframework.beans.factory.BeanFactory接口定义。 BeanFactory和相关的接口,如BeanFactoryAware,InitializingBean,DisposableBean,仍然存在于Spring中,目的是向后兼容与Spring集成的大量第三方框架 |
2 | 此容器添加了更多特定于企业的功能,例如从属性文件解析文本消息的功能以及将应用程序事件发布到感兴趣的事件侦听器的功能。 此容器由org.springframework.context.ApplicationContext接口定义。 |
注:ApplicationContext包含BeanFactory的全部功能。设计模式
构成应用程序主干并由Spring IoC容器管理的对象称为bean。 bean是一个由Spring IoC容器实例化,组装和管理的对象。 这些bean是使用您提供给容器的配置元数据建立的。数组
No | 配置和描述 |
---|---|
1 | class 此属性是必需的,并指定建立的bean类型 |
2 | name 此属性惟一地指定bean标识符 |
3 | scope 指定bean的做用范围,默认为singleton、prototype..... |
4 | constructor-arg 用于注入依赖项 |
5 | properties 用于注入依赖项 |
6 | autowiring mode 用于注入依赖项 |
7 | lazy-initialization mode 在第一次请求时建立bean实例,不是在启动时建立 |
8 | initialization method 在容器设置了bean以后全部必要属性以后调用的回调。 |
9 | destruction method 当包含bean的容器被销毁时使用的回调。 |
<?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-3.0.xsd"> <!-- 简单类定义 --> <bean id = "..." class = "..."> <!-- 这个bean的协做者和配置就在这里 --> </bean> <!-- 懒加载模式 --> <bean id = "..." class = "..." lazy-init = "true"> <!-- 这个bean的协做者和配置就在这里 --> </bean> <!-- 配置初始化方法 --> <bean id = "..." class = "..." init-method = "..."> <!-- 这个bean的协做者和配置就在这里 --> </bean> <!-- 配置销毁方法 --> <bean id = "..." class = "..." destroy-method = "..."> <!-- 这个bean的协做者和配置就在这里 --> </bean> </beans>
No | 做用域与描述 |
---|---|
1 | singlton 单例(默认) |
2 | prototype 多例 |
3 | request HTTP请求。(web) |
4 | session HTTP会话。(web) |
5 | global-session 全局HTTP会话(web) |
@Component public class SingletonBean { private String message; public void setMessage(String msg) { this.message = msg; } public void print() { System.out.println(message); } } @Component public class SpringContextUtil implements ApplicationContextAware { private static ApplicationContext context = null; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.context = applicationContext; } public static <T> T getBean(String beanName) { return (T) context.getBean(beanName); } } @SpringBootApplication public class MainApplication { public static void main(String[] args) { SpringApplication.run(MainApplication.class, args); SingletonBean singletonBean = SpringContextUtil.getBean("singletonBean"); singletonBean.setMessage("singleton"); singletonBean.print(); // singleton SingletonBean singletonBean2 = SpringContextUtil.getBean("singletonBean"); singletonBean2.print();// singleton } }
@Component @Scope("prototype") public class PrototypeBean { private String message; public void setMessage(String msg) { this.message = msg; } public void print() { System.out.println(message); } } @SpringBootApplication public class MainApplication { public static void main(String[] args) { SpringApplication.run(MainApplication.class, args); PrototypeBean prototypeBean = SpringContextUtil.getBean("prototypeBean"); prototypeBean.setMessage("prototype"); prototypeBean.print();// prototype PrototypeBean prototypeBean2 = SpringContextUtil.getBean("prototypeBean"); prototypeBean2.print();// null } }
Spring bean的生命周期很容易理解。 实例化bean时,可能须要执行一些初始化以使其进入可用状态。 相似地,当再也不须要bean并将其从容器中移除时,可能须要进行一些清理。缓存
@Component public class LifeCycleBean implements InitializingBean,DisposableBean { @Value("${lifecycle.message}") private String message; // 构造方法后message才有值 public void print() { System.out.println(message); } // 1 public LifeCycleBean() { System.out.println("Constructor"); } // 2 @PostConstruct public void init(){ System.out.println("@PostConstruct"); } // 3 @Override public void afterPropertiesSet() throws Exception { System.out.println("implements InitializingBean"); } // 4 @PreDestroy public void preDestory(){ System.out.println("@PreDestroy"); } // 5 @Override public void destroy() throws Exception { System.out.println("implements DisposableBean"); } }
BeanPostProcessor接口定义了您能够实现的回调方法,以提供您本身的实例化逻辑,依赖关系解析逻辑等。您还能够在Spring容器经过插入一个或多个实例化,配置和初始化bean以后实现一些自定义逻辑 BeanPostProcessor实现。安全
您能够配置多个BeanPostProcessor接口,您能够经过设置订单属性来控制这些BeanPostProcessor接口的执行顺序,前提是BeanPostProcessor实现了Ordered接口。服务器
BeanPostProcessors在bean(或对象)实例上运行,这意味着Spring IoC容器实例化一个bean实例,而后BeanPostProcessor接口完成它们的工做。
ApplicationContext自动检测使用BeanPostProcessor接口的实现定义的任何bean,并将这些bean注册为后处理器,而后在建立bean时由容器适当调用。
// 注意该类做用于全部的Spring Bean @Component public class BeanPostProcessors implements BeanPostProcessor,Ordered { // 2 @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (beanName.equals("lifeCycleBean")) System.out.println("postProcessBeforeInitialization " + beanName); return bean; } // 5 @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (beanName.equals("lifeCycleBean")) System.out.println("postProcessAfterInitialization " + beanName); return bean; } @Override public int getOrder() { return 1; } } @Component public class LifeCycleBean implements InitializingBean,DisposableBean { @Value("${lifecycle.message}") private String message; // 构造方法后message才有值 public void print() { System.out.println(message); } // 1 public LifeCycleBean() { System.out.println("Constructor"); } // 3 @PostConstruct public void init(){ System.out.println("@PostConstruct"); } //4 @Override public void afterPropertiesSet() throws Exception { System.out.println("implements InitializingBean"); } // 6 @PreDestroy public void preDestory(){ System.out.println("@PreDestroy"); } // 7 @Override public void destroy() throws Exception { System.out.println("implements DisposableBean"); } }
实现Ordered并设定顺序值
@Component public class BeanPostProcessors2 implements BeanPostProcessor,Ordered { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (beanName.equals("lifeCycleBean")) System.out.println("postProcessBeforeInitialization2 " + beanName); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (beanName.equals("lifeCycleBean")) System.out.println("postProcessAfterInitialization2 " + beanName); return bean; } @Override public int getOrder() { return 2; } }
bean定义能够包含许多配置信息,包括构造函数参数,属性值和特定于容器的信息,例如初始化方法,静态工厂方法名称等。
子类bean定义从父定义继承配置数据。子定义能够根据须要覆盖某些值或添加其余值。
Spring Bean定义继承与Java类继承无关,但继承概念是相同的。 您能够将父bean定义定义为模板,其余子bean能够从父bean继承所需的配置。
使用基于XML的配置元数据时,可使用parent属性指定子bean定义,并将父bean指定为此属性的值。
<!-- app-conf-1.xml --> <?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"> <bean id="animal" class="com.concretepage.Animal" init-method="initA"> <property name="name" value="Hathi"/> <property name="age" value="20"/> </bean> <bean id="elephant" class="com.concretepage.Elephant" parent="animal" init-method="initE"> <property name="age" value="30"/> <property name="location" value="Varanasi"/> </bean> </beans> <!-- 注意目前不知道如何使用注解替代:parent 和 abstract -->
public class Animal { private String name; private Integer age; public void initA() { System.out.println("Inside initA()"); } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } } public class Elephant extends Animal { private String location; public void initE() { System.out.println("Inside initE()"); } public String getLocation() { return location; } public void setLocation(String location) { this.location = location; } } public class SpringDemo { public static void main(String[] args) { AbstractApplicationContext context = new ClassPathXmlApplicationContext("app-conf-1.xml"); Elephant elephant=(Elephant)context.getBean("elephant"); System.out.println(elephant.getLocation()); // Varanasi System.out.println(elephant.getName()); // Hathi System.out.println(elephant.getAge()); // 30 context.registerShutdownHook(); } }
public class TextEditor { private SpellChecker spellChecker; public TextEditor(SpellChecker spellChecker) { this.spellChecker = spellChecker; } public void setSpellChecker(SpellChecker spellChecker) { this.spellChecker = spellChecker; } } @Configuration @Import(ConfigA.class) // 注入 public class Config{ // 方式1:构造器注入 @Bean public TextEditor textEditor(){ return new TextEditor(spellChecker()); } // 方式2:setter注入 @Scope("prototype") @Bean(initMethod = "init", destroyMethod = "cleanup") public TextEditor textEditor(){ TextEditor textEditor = new TextEditor(); textEditor.setSpellChecker(spellChecker()); return textEditor; } @Bean public SpellChecker spellChecker(){ return new SpellChecker(); } }
// 默认第一种方式最简单,无需写构造器和setter方法 @Component public class TextEditor { @Autowired // 方式一 private SpellChecker spellChecker; @Autowired // 方式二 @Qualifier("spellChecker") // 根据名称指定 public TextEditor(SpellChecker spellChecker) { this.spellChecker = spellChecker; } // 方式二 JSR-250 @Resource(name = "spellChecker") public TextEditor(SpellChecker spellChecker) { this.spellChecker = spellChecker; } @Autowired // 方式三 public void setSpellChecker(SpellChecker spellChecker) { this.spellChecker = spellChecker; } }
// 数组 @Value("${name.list}") private String[] names; // 列表 @Value("#{T(java.util.Arrays).asList('${name.list:a,b,c}')}") private List<String> list; // 列表 @Value("${name.list}") private List<String> names; // Set @Value("${name.list}") private Set<String> names; // LinkedHashSet // map @Value("#{${valuesMap}}") private Map<String, Integer> valuesMap; // map.get(key):key不存在时报错 @Value("#{${valuesMap}.key1}") private Integer valuesMapKey1; // 安全方式不会报错,设置为null(默认值) @Value("#{${valuesMap}['unknownKey']}") private Integer unknownMapKey; // 设置默认值 @Value("#{${unknownMap : {key1: '1', key2: '2'}}}") private Map<String, Integer> unknownMap; @Value("#{${valuesMap}['unknownKey'] ?: 5}") private Integer unknownMapKeyWithDefaultValue; // 过滤 @Value("#{${valuesMap}.?[value>'1']}") private Map<String, Integer> valuesMapFiltered; @Value("#{systemProperties}") private Map<String, String> systemPropertiesMap; // application.properties name.list=d,e,f valuesMap={key1: '1', key2: '2', key3: '3'}
您已经在全部章节中看到Spring的核心是ApplicationContext,它管理bean的完整生命周期。 ApplicationContext在加载bean时发布某些类型的事件。 例如,在启动上下文时发布ContextStartedEvent,并在上下文中止时发布ContextStoppedEvent。
ApplicationContext中的事件处理是经过ApplicationEvent类和ApplicationListener接口提供的。 所以,若是bean实现了ApplicationListener,那么每次将ApplicationEvent发布到ApplicationContext时,都会通知该bean。
No | Spring内置事件 |
---|---|
1 | ContextRefreshedEvent 在初始化或刷新ApplicationContext时发布此事件。这也可使用ConfigurableApplicationContext接口上的refresh()方法引起。 |
2 | ContextStartedEvent 使用ConfigurableApplicationContext接口上的start()方法启动ApplicationContext时,将发布此事件。您能够轮询数据库,也能够在收到此事件后从新启动任何已中止的应用程序。 |
3 | ContextStoppedEvent 使用ConfigurableApplicationContext接口上的stop()方法中止ApplicationContext时,将发布此事件。收到此活动后,您能够进行必要的清理工做。 |
4 | ContextClosedEvent 使用ConfigurableApplicationContext接口上的close()方法关闭ApplicationContext时,将发布此事件。封闭的环境达到其生命的终点;它没法刷新或从新启动。 |
5 | RequestHandledEvent 这是一个特定于Web的事件,告诉全部bean已经为HTTP请求提供服务。 |
public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(MainApplication.class, args); context.start(); // 发出ContextStartedEvent事件 context.stop(); // 发出ContextStoppedEvent事件 } @Component public class SpringEventListener { // 1 @EventListener public void contextRefreshedEvent(ContextRefreshedEvent event) { System.out.println("ContextRefreshedEvent "+event); } // 2 @EventListener public void contextStartedEvent (ContextStartedEvent event) { System.out.println("ContextStartedEvent "+event); } // 3 @EventListener public void contextStoppedEvent (ContextStoppedEvent event) { System.out.println("ContextStoppedEvent "+event); } // 4 @EventListener public void contextClosedEvent (ContextClosedEvent event) { System.out.println("ContextClosedEvent "+event); } }
public class CustomEvent extends ApplicationEvent { public CustomEvent(Object source) { super(source); } public String toString(){ return "My Custom Event"; } } @Component public class CustomPublisher implements ApplicationEventPublisherAware { private ApplicationEventPublisher publisher; @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.publisher = applicationEventPublisher; } public void publish() { CustomEvent ce = new CustomEvent(this); publisher.publishEvent(ce); } } // 方式1 @Component public class CustomEventHandler implements ApplicationListener<CustomEvent> { @Override public void onApplicationEvent(CustomEvent event) { System.out.println(event); } } // 方式2 @Component public class CustomEventListener { @EventListener public void listen(CustomEvent customEvent) { System.out.println(customEvent); } } @SpringBootApplication public class MainApplication { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(MainApplication.class, args); CustomPublisher customPublisher = (CustomPublisher) context.getBean("customPublisher"); customPublisher.publish(); } }
面向方面编程须要将程序逻辑分解为称为横切关注点的不一样部分。 跨越应用程序多个点的功能称为跨领域问题,这些跨领域问题在概念上与应用程序的业务逻辑分离。 有许多常见的好例子,如日志记录,审计,声明式事务,安全性,缓存等。
OOP中模块化的关键单元是类,而在AOP中,模块化单元是方面。 依赖注入能够帮助您将应用程序对象相互分离,AOP能够帮助您将交叉问题与它们所影响的对象分离。 AOP就像Perl,.NET,Java等编程语言中的触发器。
Spring AOP模块提供拦截器来拦截应用程序。例如,执行方法时,能够在方法执行以前或以后添加额外的功能
术语 | 描述 |
---|---|
Aspect 切面 | 切面是通知和切点的结合。例如,一个日志模块为了记录日志将被 AOP 方面调用。应用程序能够拥有任意数量的方面,这取决于需求。 |
Join point 链接点 | 程序执行过程当中的某个特定的点,或者说特定的时候。 |
Advice 通知 | 在 Spring AOP 中,有前置通知、后置通知、异常通知、最终通知、环绕通知 5 种。 |
Pointcut | 切点是用来匹配定位链接点的。一组一个或多个链接点,通知应该被执行。 |
Weaving 织入 | 切面应用到链接点中 |
Introduction 引用 | 引用容许你添加新方法或属性到现有的类中。 |
Target 目标类 | 被一个或者多个方面所通知的对象,这个对象永远是一个被代理对象。 |
通知 | 描述 |
---|---|
前置通知 | 在一个方法执行以前,执行通知。 |
后置通知 | 在一个方法执行以后,不考虑其结果,执行通知。 |
返回后通知 | 在一个方法执行以后,只有在方法成功完成时,才能执行通知。 |
抛出异常后通知 | 在一个方法执行以后,只有在方法退出抛出异常时,才能执行通知。 |
环绕通知 | 在建议方法调用以前和以后,执行通知。 |
// Aspect @Component @Aspect public class TimeLoggingAspect { // Advice @Around("execution(* com.littleevil.autowiredtest.aop.UserService.*(..))") // Pointcut public Object userAdvice(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("@Around: Before calculation-" + new Date()); Object result = joinPoint.proceed(); System.out.println("@Around: After calculation-" + new Date()); return result; } } // Target @Service public class UserService { public Integer multiply(int a, int b){ int res = a*b; System.out.println(a+ "*" + b +"= " + res); return res; } } @SpringBootApplication public class MainApplication { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(MainApplication.class, args); UserService userService = (UserService) context.getBean("userService"); Integer res = userService.multiply(1, 3); System.out.println(res); } }