ssm(2-1)Spring-IOC/DI
1.什么是IOC/DI
1.1 什么是IOC
全称为:Inverse of Control,控制反转,将对在自身对象中的一个内置对象的控制反转,反转后不再由自己本身的对象进行控制这个内置对象的创建,而是由第三方系统去控制这个内置对象的创建。
1.2 什么是DI
全称为Dependency Injection,依赖注入,自身对象中的内置对象是通过依赖第三方系统注入的方式进行创建。
1.3 IOC与DI的关系
IOC就是一种软件设计思想,DI是这种软件设计思想的一个实现。Spring中的核心机制就是DI。
2.基于配置
2.1设值注入
<bean id="helloworld" class="spring1.HelloWorld"> <property name="name" value="Spring"></property> </bean>
2.2 构造注入
按顺序 <bean id="car" class="spring1.Car"> <constructor-arg value="audi"></constructor-arg> <constructor-arg value="shanghai"></constructor-arg> <constructor-arg value="3000"></constructor-arg> </bean> 按类型 <bean id="car2" class="spring1.Car"> <constructor-arg value="audi" type="java.lang.String"></constructor-arg> <constructor-arg value="shanghai" type="java.lang.String"></constructor-arg> <constructor-arg value="3000" type="int"></constructor-arg> </bean> 混合使用 <bean id="car3" class="spring1.Car"> <constructor-arg value="audi" type="java.lang.String"></constructor-arg> <constructor-arg value="shanghai" index="1"></constructor-arg> <constructor-arg value="3000" type="int"></constructor-arg> </bean>
2.3 bean之间的相互引用
<!— ref=“id” --> <bean id="person" class="spring1.Person"> <property name="name" value="tom"></property> <property name="age" value="28"></property> <property name="car" ref="car3"></property> </property>
2.4 内部bean
<bean id="person" class="spring1.Person"> <property name="name" value="tom"></property> <property name="age" value="28"></property> <property name="car"> <bean class="spring1.Car"> <constructor-arg value="audi" type="java.lang.String"></constructor-arg> <constructor-arg value="shanghai" index="1"></constructor-arg> <constructor-arg value="3000" type="int"></constructor-arg> </bean> </property> </bean>
2.5 级联
<!-- 级联属性 但是在 之前必须进行相应的初始化 -->
<property name="car.maxspeed" value="3000"></property>
2.6 集合属性的构建
List <list> <ref bean="car1"/> <ref bean="car2"/> <ref bean="car3"/> <!-- 内部bean的方式书写 --> <bean class="spring1.Car"> <constructor-arg value="audi" type="java.lang.String"></constructor-arg> <constructor-arg value="shanghai" index="1"></constructor-arg> <constructor-arg value="3000" type="int"></constructor-arg> </bean> </list> Map <property name="cars"> <map> <entry key="aa" value-ref="car"> </entry> <entry key="bb" value-ref="car1"> </entry> <entry key="cc" value-ref="car2"> </entry> </map> </property> Property <property name="properties"> <props> <prop key="user">root</prop> <prop key="password">cgz12345678</prop> <prop key="jdbcurl">jdbc:mysql:///test</prop> <prop key="driverclass">com.mysql.Driver</prop> </props> </property>
备注:在不能识别构造方法时,会执行最后一个满足条件的构造方法
2.7 实例工厂(用的少)
<bean id="factory" class="cn.spring.test.InstanceFactory" ></bean> <bean id="car1" factory-bean="factory" factory-method="getCar"> <constructor-arg value='audi'></constructor-arg> </bean> private Map<String,Car> cars=new HashMap<>(); public InstanceFactory() { cars.put("audi", new Car("audi", 300000)); cars.put("ford", new Car("ford", 400000)); } public Car getCar(String name){ return cars.get(name); }
2.8 静态工厂方法(用的少)
static{ cars.put("audi", new Car("audi", 300000)); cars.put("ford", new Car("ford", 400000)); } public static Car getCar(String name){ Car car = cars.get(name); return car; } <bean id="car" class="cn.spring.test.StatiCarFactory" factory-method="getCar"> <constructor-arg value="audi"></constructor-arg> </bean>
2.9 抽像bean
<!--不需要class,abstract=“true”--> <bean id="address2" p:city="shanghai" p:street="dongfang" abstract="true"></bean> <!--parent="address2"--> <bean id="address1" class="spring3.Address"parent="address2" p:street="haoxida"></bean> <!-- depends-on="car" 初始化前需要有一个id为car的bean--> <bean id="person" class="spring3.Person"p:name="tom" p:address-ref="address1" depends-on="car"></bean>
备注:di依赖注入,一个类依赖另外一个类,ioc控制反转,手动实例化bean变成自动实例化bean
2.10util命名空间
单独bean
<!-- 配置独立的集合bean 需要导入util命名空间--> <util:list id="list"> <ref bean="car1"/> <ref bean="car2"/> <ref bean="car3"/> </util:list> <!-- 加载资源文件 --> <util:properties id="resource" location="path"></util:properties> <!-- 暴露静态field --> <util:constant id="age" static-field="Tese.age"/> <!-- 暴露属性 --> <util:property-path id="age" path="Test.age"/>
2.11 p命名空间(利用set方法)与c命名空间(利用构造方法)
<!-- p命名空间为bean的属性赋值 需要导入p命名空间 --> <bean id="person" class="spring3.Person" p:name="tom" p:address-ref="address" p:car-ref="car" ></bean>
<bean id="person" class="spring3.Person" c:name="tom" c:address-ref="address" c:car-ref="car" ></bean>
2.12 自动装配
// autowire="byName" 默认值no表示不进行自动装配,byname与bytype只能选一个 这个方法是存在弊端的 而且弊大于利,byname 根据bean的名字和当前的bean的setter风格的属性进行装配,当存在是就装配,不存在时就不装配,bytype 根据bean的类型和当前bean的属性的类型进行自动装配 若ioc存在一个以上,会抛出异常,一般很少用自动配置,在整合第三方框架的时候会使用
<bean id="person" class="spring3.Person" p:name="tom" autowire="byName"></bean>
2.13 作用域scope
singleton 默认值,表示单例模式,在这个周期类值创建一个bean只初始化一次,在创建容器的时候就已经创建好了
Prototype原型模式,在getbean的时候创建相应的bean,在容器创建的时候不会创建bean ,而是在每次getbean的时候都会创建新的bean
<bean id="car" class="spring3.Car" scope="prototype">
session对应于域对象的session,每次http session时创建一个bean对象,在web中使用时才有效
request对应于域对象的request,每次http request时创建一个bean对象,在web中使用时才有效
singleton依赖Prototype的解决方法
<bean id="student" class="cn.test.domain.Student" scope="prototype"/> <bean id="penson" class="cn.test.domain.Person" > <!--name方法名,bean指向id,lookup-method表示会重写getStudent方法,然后根据bean指向id 获取并返回,所以在getStudent方法中必须有返回值 --> <lookup-method name="getStudent" bean="student"></lookup-method> </bean>
@Component public class Person { @Lookup public Student getStudent(){ return null; } }
2.14 Spring IOC 容器对 Bean 的生命周期进行管理的过程:
通过构造器或工厂方法创建 Bean 实例,为 Bean 的属性设置值和对其他 Bean 的引用,然后将 Bean 实例传递给 Bean 后置处理器的 postProcessBeforeInitialization 方法,调用 Bean 的初始化方法init,将 Bean 实例传递给 Bean 后置处理器的 postProcessAfterInitialization方法,Bean 可以使用了,当容器关闭时, 调用 Bean 的销毁方法destroy
备注:
a)init方法和destroy方法的指定(bean无需实现任何接口和继承,单实例init在创建好实例赋值完成之后执行,多实例在每次调用时执行,单实例destroy在容器销毁时执行,多实例容器不对其进行管理)
<bean id="car" class="spring5.Car" init-method="init" destroy-method="destroy">
b)bean的后处理器
后处理需要实现 BeanPostProcessor接口,在配置文件中添加初始化 如<bean class="spring5.Mybeanpostprocesse"></bean>;
BeanPostProcessor接口其中的两个方法
postProcessBeforeInitialization 方法 Bean 的初始化赋值之后@PostConstruct之前调用,
postProcessAfterInitialization方法 Bean 初始化赋值完成之后init之后调用
备注:BeanPostProcessor这个接口比较特殊,在单例模式下,只会在第一次创建容器的时候才会执行,非单例模式下每次都会执行,BeanPostProcessor的实现类是不会对自身执行BeanPostProcessor接口的两个方法,而是对其他的进行管理
类1
public class Person { @Value("张三") String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public Person() { System.out.println("------construct------"); } @Lookup public Student getStudent(){ return null; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + '}'; } @PostConstruct public void postConstruct(){ System.out.println("---PostConstruct---"); } public void init(){ System.out.println("----init----"); } }
类2
@Configuration @ComponentScan("cn.test.life") public class MainConfigOfLifeCycle { @Bean(value = "person",initMethod = "init") public Person getPerson(){ return new Person(); } }
类3
@Component public class MyBeanPostProcessor implements BeanPostProcessor { public MyBeanPostProcessor() { System.out.println("-----------"); } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if(bean instanceof Person){ ((Person) bean).setName("李四"); } System.out.println("postProcessBeforeInitialization"+bean); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("postProcessAfterInitialization"+bean); return bean; } }
test
public class Test7 { public static void main(String[] args) { AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class); Person person = acac.getBean("person", Person.class); System.out.println(person); } }
输出结果:
----------- postProcessBeforeInitializationcn.test.config.MainConfigOfLifeCycle$$EnhancerBySpringCGLIB$$c0014f55@799d4f69 postProcessAfterInitializationcn.test.config.MainConfigOfLifeCycle$$EnhancerBySpringCGLIB$$c0014f55@799d4f69 ------construct------ postProcessBeforeInitializationPerson{name='李四'} ---PostConstruct--- ----init---- postProcessAfterInitializationPerson{name='李四'} Person{name='李四'}
备注:执行顺序Constructor >postProcessBeforeInitialization> @PostConstruct > InitializingBean > init-method>postProcessAfterInitialization
2.15 引入资源
<context:property-placeholder location="classpath:student.properties" file-encoding="gbk"></context:property-placeholder>//file-encoding编码集 <bean id="student" class="cn.test.domain.Student" p:name="${person.name}"/>
3.半配置半注解
3.1 组件
@Component: 基本注解, 标识了一个受 Spring 管理的组件
@Respository: 标识持久层组件
@Service: 标识服务层(业务层)组件
@Controller: 标识表现层组件
3.2扫描组件
<context:component-scan base-package="spring7" resource-pattern="*.class"> <!-- contextexclude-filter 排除的类 --> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/> <!--annotation:根据注解指定expression ,需要提前设置 use-default-filters="false",然后不会在默认添加filter ,这样context:include-filter才有作用,否则会默认的添加所有匹配的值--> <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/> <!-- assignable根据包名点类名指定expression --> <context:include-filter type="assignable" expression="spring7.UserRepositoryimp"/> </context:component-scan> <context:include-filter> 和 <context:exclude-filter> 子节点支持多种类型的过滤表达式
备注:默认的命名策略: 使用非限定类名, 第一个字母小写,base-package 属性指定一个需要扫描的基类包,Spring 容器将会扫描这个基类包里及其子包中的所有类 ,当需要扫描多个包时, 可以使用逗号分隔, 如果仅希望扫描特定的类而非基包下的所有类,可使用 resource-pattern 属性过滤特定的类,当 id重复是可以指定id。
3.3指定id避免id重复
@Component(“id”)等方式指定
@Primary//默认是自动动装配的首选bean,与@Autowired配合使用
@bean(value=“”)指定
@Qualifier(value)指定,可以使用在类,属性,方法,参数上等,具体见@taget
3.4自动装配
@Autowired:自动注入,具有参数require(false:表示即使没有这个属性相匹配的类也不会报错),可以放在参数,方法,属性,构造器上(具体看@target)
@Resource与@Autowired用法类似,不支持@Primary的功能
@injection与@Autowired一样,但是少了require属性,需要导入jar
3.5作用域@scope
@scope(“prototype”) singleton为默认值,表示单实例,还有request,session,prototype
Prototype原型模式,在getbean的时候创建相应的bean,在容器创建的时候不会创建bean ,而是在每次getbean的时候都会创建新的bean
session对应于域对象的session,每次http session时创建一个bean对象,在web中使用时才有效
request对应于域对象的request,每次http request时创建一个bean对象,在web中使用时才有效
4.完全基于注解(上述提到的注解不再重复)
4.1基础注解
@Configuration://配置,可以使用AnnotationConfigApplicationContext加载
@Required://例如注解在set方法上,表示必须使用set方法
@Value("李四") //指定值,可以用在set方法,参数,属性上,支持spel表达式
@PostConstruct//bean初始化赋值完成之后执行,类似init方法,还有InitializingBean接口的afterPropertiesSet()
方法也类似,只是不同的规范
@PreDestroy//容器销毁bean之前(单实例),对于多实例并不监控销毁,类似destroy方法,还有DisposableBean接口的destroy()方法
也类似,只是不同的规范
@Lazy //延迟加载,第一次调用时加载
@Bean(value="p1",destroyMethod = "destroy",initMethod = "init")/*注解具有返回值的方法,注入bean容器,destroy在销毁bean之前,init在初始化之后赋值之前,多实例不会加入bean容器管理,所以不会调用detroy方法*/
4.2@ComponentScan
@ComponentScans(): ComponentScan[] value();
@ComponentScan(value = "cn.springannocation.bean",includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Component.class})}, useDefaultFilters = false)
useDefaultFilters:在includeFilters是需要使用,因为是默认加载 ;
FilterType的枚举值有:ANNOTATION:注解;ASSIGNABLE_TYPE:类型;ASPECTJ:切面表达式(很少用);REGEX:正则;CUSTOM:自定义(需要实现TypeFilter接口)
/*自定义类型过滤器*/ public class MyTypeFilter implements TypeFilter { /*是一个一个来扫描添加的 * 返回值为true,那么就会添加到AnnotationConfigApplicationContext容器类 *metadataReader :当前正在扫描的类的信息,包含注解信息,类信息,资源信息等 *MetadataReaderFactory:可以获取其他任何类的信息 * */ @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { System.out.println(metadataReader.getAnnotationMetadata());//注解信息 System.out.println(metadataReader.getClassMetadata());//类信息,例如类名含有xxx的加入容器等 System.out.println(metadataReader.getResource());//路径等信息 return false; } }
@Configuration @ComponentScan(value = {"cn.test.domain"},includeFilters = @ComponentScan.Filter(type = FilterType.CUSTOM,value = {ScanCustom.class}),useDefaultFilters = false) public class MainConfig1 { }
4.3 @Lookup://在单实例bean依赖多实例bean时使用,一般与@Scope配合使用,单例依赖非单例
@Component public class Person { @LookupStudent public Student getStudent(){//@Lookup之后会重写该方法,每次getStudent之后具有一个新的Student(Student添加的是@scope(prototype)),该方法需要返回值 return null; } }
4.4 @PropertySource({"classpath:"})//导入多个资源文件,保存在环境变量中
@PropertySource(value = "classpath:student.properties",encoding = "gbk")//encoding编码集,引入支援
4.5 @Conditional(WindowCondition.class)/*需要实现Condition接口,满足条件的才注册bean,spring底层大量使用该注解,使用在方法上,表示当前方法需要满足条件,注解在类上,表示所有的都需要满足该条件*/
public class WindowCondition implements Condition { /** * ConditionContext: 上下文信息 * AnnotatedTypeMetadata:注释信息 */ @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();//获取bean工厂 ClassLoader classLoader = context.getClassLoader();//获取类加载器 Environment environment = context.getEnvironment();//获取上下文环境 BeanDefinitionRegistry registry = context.getRegistry();//获取bean的注册信息 registry.containsBeanDefinition("p");//容器中是否含有p if(environment.getProperty("os.name").contains("Windows")){ System.out.println(environment.getProperty("os.name")); return true; } return false; } }
4.6 @Profile("test")/*默认值defalut,表示被ioc注入,其他值在没有被激活的情况下,不能注入bean,用于数据源的切换等
注入方式:添加运行参数Dspring.profiles.active=test,或者按如下方式激活(按test方式激活)*/
@org.junit.Test public void testProfile(){ acac = new AnnotationConfigApplicationContext(); acac.getEnvironment().setActiveProfiles("test"); acac.register(MainConfig.class); acac.refresh(); String[] names = acac.getBeanDefinitionNames(); for (int i = 0; i < names.length; i++) { System.out.println(names[i]); } }
4.7@Import({Student.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})/*全类名,MyImportSelector需要实现ImportSelector接口,MyImportBeanDefinitionRegistrar需要实现ImportBeanDefinitionRegistrar接口*/
a)MyImportSelector实现ImportSelector接口,使用@Import({MyImportSelector.class})
public class MyImportSelector implements ImportSelector { //AnnotationMetadata:当前标注@Import的所有注解信息 @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { String [] strs={"cn.springannocation.bean.Cat"};//全类名的方式 return strs;//不能返回null,否则会出异常,将strs中的内容注入到bean容器 } }
b)MyImportBeanDefinitionRegistrar实现ImportBeanDefinitionRegistrar接口,使用@Import({MyImportBeanDefinitionRegistrar.class})
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { /*importingClassMetadata:当前标注@Import的所有注解信息 BeanDefinitionRegistry:使用registerBeanDefinition方法手动加载*/ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { //RootBeanDefinition是BeanDefinition子类 RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Person.class); //Person注册到BeanDefinitionRegistry中,手动注册bean registry.registerBeanDefinition("name", rootBeanDefinition);//id名为name } }
5.其他
5.1 bean工厂
public class MyFactoryBean implements FactoryBean<Cat> { @Override//获取bean public Cat getObject() throws Exception { return new Cat(); } @Override//获取bean的类型 public Class<?> getObjectType() { return Cat.class; } @Override//是否是单例 public boolean isSingleton() { return true; } }
test
@Bean("myFactoryBean") public MyFactoryBean getMyFactoryBean() { return new MyFactoryBean();//默认返回使用的是getObject方法返回的值,假如需要返回MyFactoryBean实例需要添加前缀& }
6.web加载spring
与AOP的相同
7.spel // #{}
7.1字面量
字符串 日期 数值 Boolean和null
7.2创建list集合{a,b,c}及访问
#{{a,b,c}}
集合的访问 list[index] map[key]
7.3算术
+, -, *, /, %, ^
利用+号进行字符串连接
比较 <, >, ==, <=, >=, lt, gt, eq, le, ge
逻辑 : and, or, not, |
三目运算?: (ternary), ?: (Elvis)
7.4支持正则
7.5方法的调用
与java没有区别和属性
#{address.getCity}
value="#{address.city}
7.6类型运算T
#{T(java.lang.Math).PI*80}
7.7允许使用new字段
7.8 允许使用#this 表示spel正在计算的对象
7.9 #root 引用spel的evaluationcontext的root对象
7.10 value="#{'shenz'}" 在没有找到相应的值相当于value=“shenz”
7.11 id为car的bean value="#{car}" 相当于ref="car"
7.12 集合的投影
语法 collection.【condition_expr1】
//得到的新集合时原集合的每个元素name的属性值
List.add(new person(name))
#list.!【name】