学习Spring的第3天
之前学习的手动赋值。
基本类型没有自动赋值。
这里讲的自动赋值就是指的自定义的类型。
之前我们学的都是手动赋值。
我们在Person类里面有个属性是Car car
之前在ioc.xml里面给person的car属性赋值方式:
(为Person里面的自定义类型的属性赋值)
这就是手动赋值:
现在开始看自动赋值是什么样子的?
自动赋值就是自动装配
基于XML的自动装配。
<bean id="person" class="com.rtl.bean.Person" autowire="default">
<property name="lastName" value="张三"></property>
<property name="car" ref="car"></property>
</bean>
其中autowire="default"就是自动装配。
autowire的属性值有以下几个:
1、default/no:没有自动装配
如果选择default,那么person的car属性值全为null值
2、byName:按照名字。
对象 = ioc.getBean(“car属性名”);
比如我们Person类里面有个属性是:
private Car car。
它的属性名是car,那么就会以属性名作为id去容器中找到这个组件,给他赋值。
所以,如果那个bean标签的id不是car,就不能进行自动装配了。
比如我们把之前的id=car,改为id=car01就不行了。
如果找不到就装配null
3、byType:按照属性的类型去找
以属性的类型作为查找依据去容器中找到这个组件。
对象 = ioc.getBean(Car.class);
注意:
情况一:这种按照类型去自动装配的话,如果,这个时候有两个Car类型的话,会报错
。
情况二:如果按照类型去自动装配的话,如果,容器中没有这个类型的bean标签呢?
也是会装配null
4、constructor
这个装配的方式一定不会报错!!!
按照构造器进行赋值。
1、先按照有参构造器参数的类型进行装配,成功就赋值,没有就直接为组件装配Null,也就是看看哪些bean标签是Car类型的。
2、如果按照类型找到了多个类型都是Car类型的,那就再按照id=属性名car继续匹配,有就装配,没有就null
在Person里面有个有参构造器,参数只有Car
第一种:只有一个bean,类型是Car的。
第二种:有两个bean是Car类型的。且两个的id都不是属性名car
第二种:有两个bean是Car类型的。但是有一个bean标签的id就是属性名car
注意:
我们的Person类里面有个属性是:
注意,如果是autowire=“constructor”:一定要有个专门的有参构造器:
所以,如果属性里面有个类型是List集合,那么如果按照构造器进行自动赋值,就会把容器里面所有的Book都创建,然后放在这个list集合里面去。
自动赋值:仅限于自定义类型的属性有效。
SpEL:它就是Spring Expression Language
spring的表达式语言。
使用#{}。
1、字面量
1、给Person类,增加一个属性叫做salary
2、使用运算符。
Person的lastName的值,就等于book01的书名。
3、引用其他bean
4、调用静态方法。
使用:
java.util.UUID
<bean id="person" class="com.rtl.bean.Person">
<property name="lastName" value="#{book01.bookName}"></property>
<property name="salary" value="#{123.1*2}"></property>
<property name="car" value="#{car01}"></property>
<property name="email" value="#{T(java.util.UUID).randomUUID().toString().substring(0,5)}"></property>
</bean>
value="#{T(java.util.UUID).randomUUID().toString().substring(0,5)}"
语法就是:
#{T(静态类的全类名).静态方法名(参数)}
5、调用非静态方法。
Person类的gender的值,由book01对象的getBookName()方法获取。
总的SpEL:
<?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="person" class="com.rtl.bean.Person">
<property name="lastName" value="#{book01.bookName}"></property>
<property name="salary" value="#{123.1*2}"></property>
<property name="car" value="#{car01}"></property>
<property name="email" value="#{T(java.util.UUID).randomUUID().toString().substring(0,5)}"></property>
<property name="gender" value="#{book01.getBookName()}"></property>
</bean>
<bean id="book01" class="com.rtl.bean.Book">
<property name="bookName" value="西游记"></property>
</bean>
<bean id="car01" class="com.rtl.bean.Car">
<property name="carName" value="宝马"></property>
</bean>
</beans>
通过注解分别创建Dao,Service,Controller(后面用的比较多)
之前的方式:
将javaBean 以bean标签的形式配置在xml文件里面。来创建对象。
但是,如果我们有100个javaBean呢?该怎么办?
通过给这些java bean添加某些注解,可以快速的将bean加入到ioc容器里面。
javaWeb里面的MVC
M:model 模型,对应javaBean 也就是dao
V:view 视图 对应 jsp页面
C:controller 控制层 控制的是页面的跳转逻辑,也就是servlet
1、准备好三个类:
Spring里面有四个注解。
1、@Controller:控制器层,我们推荐给控制器层(servlet包下的组件)加上这个注解。
2、@Service:业务逻辑,我们推荐给业务逻辑层的组件添加这个组件,如:BookService
3、@Repository:给数据库层(持久化层 Dao)的组件添加这个注解
4、@Component:给一些不属于以上这几个的,但是又要添加到容器的组件,比如WebUtils
某个类添加上任何一个注解,都能够快速的将这个组件加入到IOC容器里面进行管理。
2、所以给我们刚才创建好的三个类加上对应合适的注解。
注意:
注解我们可以随便加。但是推荐还是各自层添加各自对应的注解,方便cxy阅读代码。
Spring底层不会去验证我们写的组件是否如注解所说的意思。
也就是我的BookService推荐加上@Service注解,但是加上@Controller注解也不会报错啊!
快速使用以上四个注解的形式,将对象加入到容器里面步骤:
1、给你需要添加的组件的上面加上四个注解的唯一一个。
2、需要告诉Spring,让它自动帮我们扫描这些我们加了注解的组件。
怎么告诉它呢?
答:依赖context,名称空间。
3、使用注解的形式将组件添加进容器里面,必须要导入AOP包,否则会报:类没有找到的异常。
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
</beans>
<context:component-scan
这个代表自动组件的扫描
base-package=""
这个表示 指定扫描的基础包,把基础包及这个包下面的所有加了注解的类自动扫描进入ioc容器中
这里写包名的时候,不要只写一级,com。因为很多外部引入的包也是com开头,这样就会引入很多很多组件。
至少精确到两级。
<context:component-scan base-package="com.rtl"></context:component-scan>
测试,这些组件有没有真正进入容器:
注意:id默认就是这些组件的类名的首字母小写。
使用注解的形式将组件添加进容器里面,必须要导入AOP包,否则会报:类没有找到的异常。
快速使用以上四个注解的形式,将对象加入到容器里面步骤:
1、给你需要添加的组件的上面加上四个注解的唯一一个。
2、需要告诉Spring,让它自动帮我们扫描这些我们加了注解的组件。
怎么告诉它呢?
3、使用注解的形式将组件添加进容器里面,必须要导入AOP包,否则会报:类没有找到的异常。
使用注解加入到容器中的组件和使用配置加入到容器的组件行为都是默认一样的。
1、组件的id 默认就是类名首字母小写。
2、组件的作用域:默认都是单例的。
基于组件的这些默认的行为,进行调整。
我不喜欢这些默认的
1、比如不希望它的id是类名的首字母小写,喜欢自定义。
package com.rtl.dao;
import org.springframework.stereotype.Repository;
@Repository("bookdaohaha")
public class BookDao {
}
报错:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'bookDao' is defined
正确的写法:
2、修改默认是单实例的这个设置。
我们希望是多实例的,
package com.rtl.dao;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Repository;
@Repository("bookdaohaha")
@Scope("prototype")
public class BookDao {
}
并不是注解可以解决所有事情。
比如:
我想把那些不是我写的类也加入进容器里面进行管理。
注解和bean标签合并使用。
<context:component-scan base-package="com.rtl"></context:component-scan>
现在默认扫描 base-package=“com.rtl”
com.rtl包下面的所有类。
但是我们现在要指定,扫描包的时候,不要扫描哪些类。扫描的时候,排除一些不要的东西。
exclude-filter
type="annotation" 表示 指定排除的规则,这是按照注解进行排除。标注了注定注解的类,我们就不要扫描。
expression="org.springframework.stereotype.Controller"
这个就表示指定注解的全类名。
<context:component-scan base-package="com.rtl">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
type=“annotation” 表示 指定排除的规则,这是按照注解进行排除。标注了注定注解的类,我们就不要扫描。
在指定排除的规则的时候,有几种类型。
常见的就是annotation和assignable
type=“assignable”
这个是指定排除某个具体的类。也就是按照类进行排除。
明确指定某个类不需要扫描
<context:component-scan base-package="com.rtl">
<context:exclude-filter type="assignable" expression="com.rtl.servlet.BookServlet"/>
</context:component-scan>
这就是明确指定不要扫描这个组件:
com.rtl.servlet.BookServlet
要写类的全类名
include-filter
我只要扫描哪些组件。
下面就是:
只扫描我们指定的组件。include-filter
这里一定要禁用默认规则才有用。
use-default-filters=“false”
<context:component-scan base-package="com.rtl" use-default-filters="false">
<context:include-filter type="assignable" expression="com.rtl.dao.BookDao"/>
</context:component-scan>
这样就只会自动扫描BookDao组件了,其他的组件,它不会自动扫描。
这样就只会自动扫描BookDao和BookService两个组件了
默认还是扫描所有的组件。
DI 依赖注入的表现
使用@Autowired注解实现根据类型实现自动装配
属性的自动注入而且不用写任何的get/set方法。
只需要一个注解 @Autowired自动装配
1、这个是最底层的方法
和数据库进行交互
2、业务层:
3、控制层
测试:
我们都使用了@Autowired的注解,来给属性赋值。
现在测试的时候,调用doGet方法,看看能不能联动调用。
不会出现空指针异常。
测试:
配置文件:
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<context:component-scan base-package="com.rtl" ></context:component-scan>
</beans>
测试结论:发现所有的方法都联动调用,没有出现空指针异常。
讲一下原因:
问:为什么bookDao对象不是空指针呢?
第一点:
第二点:
加上注解@Autowired Spring会自动的为这个属性赋值。
加了注解@Repository和context命名空间自动扫描这些加了四个注解的类,所以bookDao对象不是null
如果资源类型的bean不止一个,默认根据@Autowired注解标记的成员变量名作为id查找bean,进行装配。
我们来看看
@Autowired
private BookService bookService;
是怎么给bookService赋值赋上去的呢?
1、先按照类型去容器中找到对应的组件。
bookService = ioc.getBean(BookService.class);
那么按照类型去容器里面找,会出现三种情况。
1、找到一个:赋值
2、找到多个:比如父子类。继续找,按照变量名作为id继续匹配。(不会报错)
有两个类:
一个是父类:BookService
另一个是子类:BookServiceExt extend BookService
那么第一步按照类型BookService.class,就会找到两个:BookService和BookServiceExt
第二步:根据id去找,
@Autowired
private BookService bookService;
这个属性名就默认作为id去找
然后两个类BookService和BookServiceExt 都会有自己默认的id
bookService和bookServiceExt
所以:因为bookService == bookService
所以。就匹配BookService
3、没找到:抛异常 NoSuchBean…
如果根据成员变量名作为id还是找不到bean,可以使用@Qualifier注解明确指定目标bean的id
@Autowired
private BookService bookService2;
1、根据类型是:BookService
找到两个类
分别是:
public class BookService和
public class BookServiceExt extends BookService
2、拿属性名 bookService2去找
但是这两个类的id是bookService和bookServiceExt
都没有匹配上
bookService2 != bookService
bookService2 != bookServiceExt
j继续报错。
原因就是我们默认按照成员的变量名作为id去匹配。
@Autowired
private BookService bookService2;
解决办法:
使用注解@Qualifier(“bookService”)
@Qualifier("bookService")
@Autowired
private BookService bookService2;
让Spring不要用成员变量名来做id去匹配,而是自己指定。
现在Spring默认就是给属性添加注解@Autowired,默认情况Spring会一直帮我们找下去,找到就装配。找不到直接就抛异常了。
这样很粗暴。
@Autowired(required = false)
private BookDao bookDao;
@Autowired(required = false)
这个required = false
就代表,如果实在没有找到,不要抛异常,而是赋值null。
bookDao == null
@Autowired 这个注解也可以用在方法上
1、这个方法会在容器启动的时候,自动执行,无需调用它。
2、这个方法所需的每一个形参都会自动赋值。
@Autowired
public void haha(BookServlet bookServlet){
System.out.println("这个方法上有@Autowired的注解");
System.out.println("参数bookServlet有值:"+bookServlet);
}
测试类:
参数名上也可以使用@Qualifier("")注解。当你不想使用参数名来作为id进行匹配的时候。
参数上也可以标注解(Spring MVC到处都是)。
@Autowired(required = false)
public void haha(@Qualifier("bookServiceExt") BookServlet bookServlet){
System.out.println("这个方法上有@Autowired的注解");
System.out.println("参数bookServlet有值:"+bookServlet);
}
自动装配的注解除了: @Autowired之外
还有@Resource
@Resource VS @Autowired
现在加入使用@Resource:
1、和数据库打交道:
2、业务逻辑:
3、控制层:
测试联动?
表明:
使用@Resource注解照样可以进行自动装配。
我们讲的自动装配都指的是:自定义类型。
@Resource
@Autowired
区别:
1、@Autowired是Spring自己的注解,最强大
2、@Resource 是J2EE的。java的标准
因为@Resource是标准,所以它的扩展性很强。即使你的容器用的不是Spring。切换为另一个框架,@Resource照样适用。
@Autowired是Spring自己的注解,所以@Autowired离开了Spring就没办法使用了。
注意地方:
1、你想要使用@Autowired自动装配,那你就先要把一些组件带上那四大注解中的某一个才行。(先要成为人家的会员)
2、