文章目录
spring模块
Test:Spring的单元测试模块
core container(核心容器IOC):
aop+aspect(面向切面变成模块):
数据访问:spring数据库访问模块:
Web:Spring开发web应用的模块
spring运行还需要一个人重要的日志文件:
commons-logging
SpringIOC发展史:
以前我们使用对象需要自己new
Student student = new Student();
student.setXxx();
现在我们使用IOC
IOC(控制反转)也可以称之为DI(依赖注入)IOC是一种思想,DI是实现:
主动地new变为被动的从容器中接收
控制反转:将 创建对象、属性值 的方式 进行了翻转,从new、setXxx() 反转为了 从springIOC容器getBean()
依赖注入:将属性值 注入给了属性,将属性 注入给了bean,将bean注入给了ioc容器;
细节:
1.那么对象是什么时候创建好的呢?
容器中对象的创建在容器创建完成的时候就已经创建好了(默认为单例,如果为多例,则在getBean后才会创建)
2.多次getBean得到的是一个对象么?
同一个组件在ioc容器中是单实例的。
3.ioc容器在创建这个组件对象的时候,(property)会利用setter方法为javaBean的属性进行赋值
4.javaBean的属性名是由什么决定的?
getter/setter方法决定的,去掉set方法的set,再把前面的字母大写就是property中name的值
依赖注入3种方式:
1.set注入:通过setXxx()赋值
赋值,默认使用的是 set方法();
依赖注入底层是通过反射实现的。
property中的name要写的和setXxx中的一样才能实现反射
根据Bean的id从IOC容器中获取bean实例
<property name="name" value="zhangshan"></property>
<property name="age" value="12"></property>
Person person = (Person) ioc.getBean(“person01”);
此时是需要强转的
根据Bean的类型从IOC容器中获取bean实例
Person person = ioc.getBean(Person.class);
这个时候是不需要强转的,但是当IOC中有两个Person的bean实例的时候会报错
在编写bean时,value当property的子元素时他需要加“”,但是当value当标签时,他不需要加“”,而且还可以使用type指定具体是什么类型的,
2.构造器注入:通过构造方法赋值
<constructor-arg name="name" value="zhuzhu"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
需要注意:如果 < constructor-arg>的顺序 与构造方法参数的顺序不一致,则需要通过type或者index或name指定。
无论是String还是Int/short/long,在赋值时都是 value=“值” ,
因此建议 此种情况 需要配合 name\type进行区分
3.p命名空间注入
引入p命名空间
xmlns:p=“http://www.springframework.org/schema/p”
<bean id="course" class="org.lanqiao.entity.Course" p:courseHour="300" p:courseName="hadoop" p:teacher-ref="teacher">
他也是使用的set方法进行的赋值
注意多个 p赋值的时候 要有空格。
复杂赋值
1.当Person中又有了car类,而且要对某一个值赋空的时候,null是在property中又有了一个单独的< null/>标签
<bean id="person03" class="com.Person">
<property name="age" value="21"></property>
<property name="name" >
<null/>
</property>
<property name="car">
<bean class="com.Car">
<property name="name" value="兰博基尼"></property>
<property name="price" value="111111"></property>
</bean>
</property>
</bean>
注意点:内部bean的id值写和没写是一样的,都不能获取到
2.各类集合的set方法注入
list:
<list>
<value>足球</value>
<value>篮球</value>
<value>排球</value>
</list>
set:
<set>
<value>足球</value>
<value>篮球</value>
<value>排球</value>
</set>
map:
<map>
<entry>
<key>
<value>football</value>
</key>
<value>足球</value>
</entry>
<entry>
<key>
<value>basketball</value>
</key>
<value>篮球</value>
</entry>
<entry>
<key>
<value>paiball</value>
</key>
<value>排球</value>
</entry>
</map>
数组:
<array>
<value>足球</value>
<value>篮球</value>
<value>排球</value>
</array>
props:
<props>
<prop key="football">足球</prop>
<prop key="basketball">篮球</prop>
<prop key="paiball">排球</prop>
</props>
这里的value是property的属性
< property name=“courseName” value=“java”>< /property>
value也可以当做标签
赋其他值用value,赋空值直接用标签< null/>
当我们想要写的map让其他bean调用,此时我们可以配置spring配置文件头部的xmlns添加util空间。
3.当个bean中的对象的值有些些相同时,可以在bean中进行parent进行“继承”(但实际上并不是继承,因为两个对象的类型都是一样的,只不过是赋值了一份而已),此时第二个bean中就继承了第一个的值,若想该哪个值再往下进行重新赋值即可。当然我们也可以吧一个bean专门设置为抽象bean(在bean标签中关键字abstract=“true”),专门供其他bean使用
Bean的作用域:
一般只用以下两种:singleton和prototype
singleton(不指定的话,默认为单例):容器在初始化时,就会创建对象(唯一的一个);以后再getBean时,不再产生新的bean。singleton也支持延迟加载(懒加载):可以使用 @Lazy 来进行设置,只有getBean时才会创建对象
prototype:容器在初始化时,不创建对象;只是在每次使用时(每次从容器获取对象时 ,context.getBean(Xxxx)),再创建对象;并且 每次getBean()都会创建一个新的对象。
工厂模式
FactoryBean(工厂Bean)
FactoryBean是一个接口,实现它并且重写他的方法
1.准备bean。实现类和重写方法
2.注册bean。注册到@Bean中
可以根据重写的方法来设置单例多例,而且工厂默认不管是单例还是多例,都会到getBean的时候才会创建对象,
工厂模式适用的一些场景(不仅限于以下场景):
- 对象的创建过程/实例化准备工作很复杂,需要初始化很多参数、查询数据库等。
2.类本身有好多子类,这些类的创建过程在业务中容易发生改变,或者对类的调用容易发生改变。
注意:需要通过&区分 获取的对象是哪一个 : 不加&,获取的是最内部真实的Apple;
如果加了&,获取的 是FacotryBean
引用外部属性文件
1.需要添加context名称空间
2.还要写加载内部类:
<context:property-placeholder location="classpath:database.properties"/>
数据库连接池作为单例是最好的,一个项目就一个连接池,连接池里面管理很多连接,连接是直接去连接池中拿,所以我们可以让spring帮我们创建连接池。
我们习惯于把数据库的连接配置放在properties文件中
自动装配(只适用于 ref类型 )
约定优于配置
自动装配:
<bean ... class="org.lanqiao.entity.Course" autowire="byName|byType|constructor|no" >
byName本质是byId
byName: 自动寻找:其他bean的id值=该Course类的属性名(一般就用这个)
byType: 其他bean的类型(class) 是否与 该Course类的ref属性类型一致 (注意,此种方式 必须满足:当前Ioc容器中 只能有一个Bean满足条件 )
constructor: 其他bean的类型(class) 是否与 该Course类的构造方法参数 的类型一致;此种方式的本质就是byType
可以在头文件中 一次性将该ioc容器的所有bean 统一设置成自动装配:
<beans xmlns="http://www.springframework.org/schema/beans"
...
default-autowire="byName">
自动装配虽然可以减少代码量,但是会降低程序的可读性,使用时需要谨慎。
sqel表达式
可以在value中用#{}进行赋值
${}取的是配置文件中的值
使用注解IOC容器中存放bean
想要使用注解:
1.就要导入AOP的包,
2.在xml文件中加入扫描注解所在的包
- 我们也可以对扫描的包就行一些排除
上面的是排除哪些包,下面的是只扫描哪些包
使用注解的id默认为该类的首字母小写,或者在注解后面加括号,给id重新命名
注解的作用域范围:默认就是单例
自动注入
@Autowired自动注入
当在某个类中定义了属性(这个属性是自定义类型的)上写了@Autowired,想要生效,这个属性对应的类必须得在容器中(并不是必须得用bean,扫描进入容器的也算是在容器中)。
当容器中有 两个类都是该属性时(一个继承另一个),此时会按照加了这个@Autowired的属性的变量名和这两个类的id进行比较(这两个类的id默认为首字母小写),
若我们没有按照首字母小写的当做变量名的时候,这两个类最终都不会被匹配上去,所以我们可以用:
@Qualifier("")来指定一个名当做id,不让spring使用变量名作为id。这里其实也可以把要自动注入的那个类的注解加个自定义id,如@Service(“book”)
注意:当把该自定义的类注入到容器中时,该类的方法也会随之注入进去
@Autowired自动注入找不到时就会报错,此时我们可以设置
@Autowired(required=false)
@Resourse,java的标准,他比spring 少了@Autowired(required=false)这个功能
如果面试问到@Resourse和@Autowired的区别:Resourse的扩展性更强,如果换到其他的容器,Resourse还可以用,Autowired就不可以用了
当@autowired放到方法上面时:如果里面没有参数,spring容器会在类加载完后执行一次这个方法;
如果方法中有参数的话,还会从容器中自动注入这个方法的参数,然后执行一次这个方法。
@autowired(required = false)作用在方法上,当方法有参数时,如果IOC容器中有方法参数的对象,那么会自动注入并执行方法一次;如果IOC容器中没有方法的参数对象,那么这个方法不会被执行,不管这个方法上有多少个参数,只要有一个参数对象是IOC容器中没有的,这个方法便不会被执行。如果方法没有参数,那么会被执行一次。
使用注解向IOC容器中存放bean
1.我们需要创建一个配置类,在上面加上@Configuration,表示这个是配置类
2.然后在@ComponentScan(basePackages={""}),写我们要扫描的包
3.然后将
ApplicationContext ioc=new ClassPathXmlApplicationContext("application.xml");
换成
ApplicationContext ioc=new AnnotationConfigApplicationContext(配置类.class)
1.此类属于非三层组件的方法
存bean
@Configuration
public class MyCourse {
@Bean//id默认是方法名,类型是方法的返回值类型
public Course myaaCourse(){
Course course=new Course("java",4);
course.setTeacher(new Teacher("张三"));
return course;
}
}
取bean
public static void testCourse2(){
ApplicationContext context = new AnnotationConfigApplicationContext(MyCourse.class);//这里括号里放的是前面写的那个类的名字,也可以用String 类型的,这里暂时用的是反射
//set,构造器用的是ClassPathXmlApplicationContext("applicationContext.xml")
Course course=(Course)context.getBean("myaaCourse");
System.out.println(course);
}
注解形式 给IoC容器中存放Bean:
1.有@Configuration的注解(配置类)(上面的那种方式)
2.三层组件加入IOC容器: 给个各类加 注解 、 扫描器识别注解所在包
给三层组件 分别加注解(@Controller、@Service、@Repository -> @Component)将注解所在包 纳入ioc扫描器(ComponentScan)
2.纳入ioc扫描器的两种方式:
①xml配置文件 :
<context:component-scan base-package="com.yanqun" >
</context:component-scan>
②注解扫描器:@Configuration记住要加上这一句
在@ComponentScan(value="")""中加要扫扫描的文件,
这里也可以给扫描器指定规则:
过滤类型:FilterType(ANNOTATION,ASSIGNABLE_TYPE,CUSTOM)
在@ComponentScan()中加入(排除 ) excludeFilters={},{}里写
{@ComponentScan.Filter(type=FilterType.ANNOTATION,value=Service.class)}
也可以includeFilters={},但是这里需要注意,有默认行为,需要在最后
可以通过useDefaultFilters = false禁止
FilterType.ASSIGNABLE_TYPE可以对具体的类进行制定规则
CUSTOM自定义:自己定义包含规则
回顾给IoC加入Bean的方法
注解 :全部在@Configuration配置中设置:
三层组件: 扫描器 + 三层注解
非三层组件: ① @Bean+返回值
②@import(忘完了)
③FactoryBean(工厂Bean)
@import使用:
①直接编写到@Import中,并且id值 是全类名
②自定义ImportSelector接口的实现类,通过selectimports方法实现(方法的返回值 就是要纳入IoC容器的Bean) 。 并且 告知程序 自己编写的实现类。 @Import({Orange.class,MyImportSelector.class})
③编写ImportBeanDefinitionRegistrar接口的实现类,重写方法
@Import({Orange.class,MyImportSelector.class,ImportBeanDefinitionRegistrar.class})
Bean的生命周期:
方法一:(其实算是两种)
先在Course.java中写
init (), destroy()方法
xml:
init-method=“init” destroy-method=“destroy”
注解:
@Bean(value=“myaaCourse”,initMethod = “init”,destroyMethod = “destroy”)
由此得出了IOC容器在初始化时,会自动创建对象(构造方法) ->init ->…->当容器关闭时 调用destroy…(这是单例。多例时,容器关闭不会销毁)
singleton 他默认的是当容器创建的时候就直接创建对象,所以对象的创建会比init先
注意:在注解@bean中每个属性之间有逗号,而xml文件中的bean没有逗号
方法二:
写一个类可以是属于三层的,也可以不是
上面加 @Component注解
给初始化方法加@PostConstruct、给销毁方法加@PreDestroy
记得写扫描器
如果要获取@Component注解中的bean(中的某个方法),那么该Bean的名字就是@Component(value=“xxx”)的value值(xxx写这个类的名字)
方法三:两个接口
接口:适用于三层组件(扫描器+三层组件)
InitializingBean初始化
DisposableBean 销毁
初始化:只需要 实现InitializingBean中的afterPropertiesSet()方法
销毁:实现DisposableBean 中的destroy()方法
方法四:(给容器中的所有Bean加初始化、销毁)一个接口
接口:适用于三层组件
接口BeanPostProcessor:拦截了所有中容器的Bean
它可以对其他bean进行拦截,进行修改信息等操作;
如果是注解形式 , 随便写一个方法 ,然后加上相应注解即可
如果是接口形式,必须 实现接口中规定的方法
依赖注入: 当写了@Autowired时,该类会往IOC容器中有没有该类型的(一定是自己创建的类型,而不是普通数据类型和String 类型),
如果将@Autowired写在类的前面,则他没有使用set方法
如果将@Autowired写在set方法前,则他使用set方法
@Autowired 根据类型匹配:
三层注入方式/@Bean+返回值
1.如果有多个类型相同的,匹配哪个?
报错。 /默认值@primary
2.能否根据名字匹配?
可以,结合 @Qualifier(“stuDao2”)使用。
3.如果有0个类型相同,默认报错;可以修改成不注入(null),@Autowired(required=false)
自动注入方式一: @Autowired (Spring) ,默认根据类型
自动注入方式二 @Resource(JSR250),默认根据名字 (如果 有名字,根据名字匹配;如果没有名字,先根据名字查找,如果没找到,再根据类型查找);也可以通过name或type属性 指定根据名字 或类型找。
自动注入方式三: @Inject(JSR330),额外引入javax.inject.jar,默认根据类型匹配
自动注入方式四:@Reference
@Reference是dubbo的注解,也是注入,他一般注入的是分布式的远程服务的对象,需要dubbo配置使用。
component和bean的区别
bean是方法级别的注解,一般使用在@Configuration中,也可以用在@Component注解的类里,添加的bean的id值为方法名,类型为方法的返回值,
@component和Controller,Service,Repositor作用也都一样,都是加在类上面的,此时会经过扫描加入到容器中
@Bean的用途则更加灵活
当我们引用第三方库中的类需要装配到Spring容器时,则只能通过@Bean来实现