官网:https://spring.io/projects/spring-framework
历史:澳大利亚人Rod Johnson,2004年发布了Spring Framework,同年成立 interface21公司, 2007 年更名为 SpringSource,2009年被 VMWare 收购,2013 年Spring 项目被转到VMware投资的 Pivotal 软件公司。
概念
Java Bean:需要交给容器管理的对象
Spring Bean容器:根据配置文件生成并管理Bean实例 的框架对象,又叫Spring IoC容器
IoC:Inversion of Control,控制反转;主程序从Bean容器中获取所需Bean,而不是自己生成和管理;即对Bean的控制权从主程序反转到了Bean容器。
DI:Dependency Injection,依赖注入;Bean A 依赖 Bean B(即A持有一个B),主程序从Bean容器中获取A时,容器会自动把B注入到A中。
AOP:Aspect Oriented Programming,面向切面的编程。通过动态代理,在主程序前后织入切面逻辑(拦截器)
EJB:企业级Java Bean,是Sun出品的Java ee框架,运行在EJB容器上,例如Weblogic、Websphere
Spring MVC:Spring MVC 是 Spring Framework的一个特性
SSH:Struts + Spring Framework + Hibernate
SSM:Spring MVC + Spring Framework + MyBatis
动态代理 结合 拦截器
// 拦截器接口
public interface Interceptor {
public boolean before();
public void around();
public void after();
}
// 动态代理类中
private Interceptor interceptor; // 持有一个拦截器
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
if(interceptor.before()) { // befroe()返回true,则执行 invoke,否则执行 around
result = method.invoke(object, args);
}
else{
interceptor.around();
}
interceptor.after();
return result;
}
动态代理 结合 责任链模式
// 服务对象(被代理对象)
BuyHouse target = new BuyHouseImpl();
// 动态处理器实例,接收一个拦截器
DynamicProxyHandler dph = new DynamicProxyHandler(buyHouse, interceptor1);
// 代理实例
BuyHouse proxy1 = (BuyHouse) Proxy.newProxyInstance(target .getClass().getClassLoader(), target.getClass().getInterfaces(), dph);
// 前一个代理实例 作为 后一个代理实例的服务对象
dph = new DynamicProxyHandler(proxy1, interceptor1);
BuyHouse proxy2 = (BuyHouse) Proxy.newProxyInstance(proxy1.getClass().getClassLoader(), proxy1.getClass().getInterfaces(), dph);
proxy2.buyHosue(); // proxy2代理了proxy1,proxy1代理了target
术语
Context:Bean容器
profile:场景
总结
Bean的装载:
Bean的获得:ApplicationContext.getBean()、@Autowired自动注入,xml中 ref 引用
IDEA 配置 Spring
1、在Maven中央仓库或Maven搜索服务上查找Spring,配置Maven依赖
org.springframework
spring-core
5.0.8.RELEASE
org.springframework
spring-beans
5.0.8.RELEASE
org.springframework
spring-context
5.0.8.RELEASE
2、右键 -> Add Frameworks Support -> 勾选Spring,勾选自动生成spring-config.xml
通过XML配置 装配Bean
1、Bean类
2、Bean配置文件,spring-config.xml
value-prop-1
value-prop-2
value-prop-3
value-set-1
value-set-2
value-array-1
value-array-2
value-array-3
3、使用
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
// 读取Bean配置文件,初始化Bean,依赖注入,放入Bean容器ApplicationContext
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config.xml");
Country country = (Country) ctx.getBean ("country"); // 通过Bean Id 得到 Bean实例
System.out.println(country.getCountryName());
Bean生命周期
初始化开始 ->
调用Bean类的setBeanName,来自BeanNameAware接口 ->
调用Bean类的setBeanFactory(BeanFactory),来自BeanFactoryAware接口 ->
调用Bean类的setApplicationContext(ApplicationContext),来自ApplicationContextAware接口 ->
调用Bean类的postProcessBeforelnitialization(bean,beanName),来自BeanPostProcessor接口 ->
初始化属性 ->
调用Bean类的afterPropertiesSet(),来自InitializingBean接口 ->
调用Bean类的自定义初始化方法 ->
调用Bean类的postProcessAfterlnitialization(bean,beanName),来自BeanPostProcessor接口 ->
生存期 ->
调用Bean类的自定义销毁方法 ->
调用Bean类的destroy,来自DisposableBean接口
通过注解类 装配Bean
1、Bean类
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.annotation.Autowired ;
@Component(value="city1") // 相当于xml里的 bean id
public class City {
@Value("1"); // 相当于xml里的value
private Long id;
@Value("city_1")
private String cityName ;
@Value("86")
private String countryCode;
@Autowired // 自动装配依赖,即容器自动查找同类Bean,并注入
private Country country;
}
自动装配的歧义性:假设Country有两个子类Country1 和 Country2,且都被装配。那么容器无法确定给City注入哪个Country
消除歧义:
方法1、给Country1 增加注解 @Primary,则优先注入Country1
方法2、@Autowired
@Qualifier("country1") // 通过Bean Id 指明 依赖
private Country country = null;
2、Bean配置类,BeanConfig.java
import org.springframework.context.annotation.ComponentScan;
@Import({BeanConfig1.class, BeanConfig2.class}) // 导入其他BeanConfig
@ComponentScan(basePackages={"com.maven.hogen",”com.maven.hogen.bean"}, // 要扫描包
basePackageClasses={City.class, Country.class}) // 要扫描的类
public class BeanConfig {
}
3、使用
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
// 扫描BeanConfig里配置的目标Bean,并装载到Bean容器
ApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);
City city = context.getBean(City.class );
System.out.println(city .getId()) ;
通过注解方法 装配Bean
前提:方法所在Java类能被扫描到
@Bean(name="city1", initMethod="init", destroyMethod="destroy") // name 为 Bean Id
// 扫描到这时,会调用该方法,将其返回值作为Bean,进行装载
public City initCity () {
return city;
}
装配方式选择:自定义的类用注解装配,第三方的类用XML装配
混合装配,同时通过XML和注释装配
@ComponentScan(basePackages={"com.maven.hogen"})
@ImportResource({"classpath:spring-dataSource.xml"})
public class ApplicationConfig {
}
场景设置
声明场景
Java中定义Bean的地方:@Profile("dev")
XML中加一层:
激活场景
Spring MVC配置文件中:
Bean作用域
1、单例(Singleton),始终只有一个
2、会话(Session),每个会话一个
3、请求(request),每次请求一个
4、原型(prototype),每次从容器中获取,都得到新的一个
设置作用域:@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
Spring EL(表达式语言)
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
ExpressionParser parser = new SpelExpressionParser();
// 解析字符串,执行里面的语句
Expression exp = parser.parseExpression(" 'hello world'.charAt(0) ");
// 得到语句的值
char ch = (Character) exp.getValue();
System.out.println(ch);
// 调用getter的简写,.bytes 相当于 .getBytes()
exp = parser.parseExpression (" 'hello world'.bytes ");
exp = parser.parseExpression("cityName");
// 相当于取 city.cityName
String cityName = (String) exp.getValue(city) ;
// 把对象包装成 执行语句的上下文
EvaluationContext ctx =new StandardEvaluationContext (city);
parser.parseExpression("cityName").setValue(ctx, "Beijing"); // 设置属性
cityName = parser.parseExpression("cityName").getValue(ctx , String.class); // 取值属性
// 将上下文设置为列表
ctx.setVariable("list", list);
// 通过表达式去读/写环境变量的值
parser.parseExpression("#list[1]").setValue(ctx, "value1"); // 设置列表元素
System.out.println(parser.parseExpression("#list[1]").getValue(ctx)); // 取值列表元素
在一个Bean中引用(注入)另一个Bean的值
@Component("beanName")
public class Country{
@Value("#{city}") // 引用Bean Id为city的Bean,来给city1初始化
private City city1;
@Value("#{city.id}") // 引用属性
private Long id;
@Value("#{city.getCityName()}") // 引用方法
private String cityName;
@Value("#{T(Math).PI}") // 引用静态成员
private double pi;
@Value("#{city.cityName + city.note}") // 运算后注入
private String str;
}
Spring AOP
术语
Aspect:切面,即一组拦截器组成的类
Advice:拦截器
Weaving:织入
Pointcut:切点,即被拦截的方法
Join point:连接点,即切点中拦截器被织入的位置
AOP:将切面的拦截器织入到切点里的连接点
Spring AOP除了能处理用户提供的切面,同时提供一些常用切面,例如数据库事务
Spring AOP 组成
1、Bean
2、切面类
@Aspect // 注解为切面类
@Order(1) // 一个切点可能被多个切面拦截,不注明顺序的话,是无序的
public class CityAspect{
// * 表示任意返回值,(..) 表示任意参数
@Before("execution(* com.maven.hogen.city.printCity( .. ))”) // 位置+切点 = 连接点
public void before () { // do something } // 拦截器
// 把切点的入参 传递给 拦截器
@After("execution(* com.maven.hogen.city.printCity( .. )) && args(cityId,cityName)")
public void after(int cityId, String cityName) { // do something }
@AfterReturning("execution(* com.maven.hogen.city.printCity( .. ))”)
public void afterReturning () { // do something }
@AfterThrowing("execution(* com.maven.hogen.city.printCity( .. ))”)
public void afterThrowing() { //do someting }
@Around("execution(* com.maven.hogen.city.printCity( .. ))”) // 环绕拦截
public void around(ProceedingJoinPoint jp) {
System.out.println (”before .. ..”) ;
try {jp.proceed();}
catch (Throwable e) { e.printStackTrace();}
System.out.println (”after ....”);
}
3、AOP配置类 AopConfig.java
@Configuration
@EnableAspectJAutoProxy
@ComponentScan("com.maven.hogen")
public class AopConfig {
@Bean
public RoleAspect getRoleAspect() { return new CityAspect();}
}
4、使用
ApplicationContext ctx =new AnnotationConfigApplicationContext(AopConfig.class) ;
引入(Introduction)
@Aspect
public class CityAspect{
// 把City代理成一个CityVerifier的实现类,即cityVerifier是City的代理,并拥有CityVerifierImpl里实现的方法
@DeclareParents(value="com.maven.hogen.City+”, // + 表示增强
defaultimpl=CityVerifierImpl.class) // CityVerifierImpl 是 CityVerifier 的实现类
public CityVerifier cityVerifier; // CityVerifier 是个接口
}