Spring02

Spring框架

Spring是一个开源的Java/Java EE全功能栈full-stack的应用程序框架,是一个轻量级的控制反转IoC和面向切面AOP的容器框架。
Spring通过IoC/DI容器,关注点便放到了需要实现的业务逻辑上;对AOP的支持则让动态增强业务方法。编写普通的业务逻辑Bean是非常容易而且易于测试的,因为它能脱离JavaEE容器单独进行单元测
试;最后的一步便是在Spring框架中将这些业务Bean以XML配置文件或Annotation注解或JavaConfig配置类的方式组织起来,它们就按照预定的目标正常工作了

  • 目的:解决企业应用开发的复杂性,JavaEE的一站式解决方案
  • 功能:使用基本的JavaBean代替EJB,并提供了更多的企业应用功能
  • 范围:任何Java应用,不适合做分布式系统开发(EJB)

基于xml配置

  • bean元素:使用该元素描述需要spring容器管理对象
  • name属性:给被管理的对象起个名字,获得对象时getBean(“name值”)
  • class属性:被管理对象的完整类名
  • id属性:与name属性一模一样,名称不可重复,不能使用特殊字符

在配置文件中使用id属性定义受管bean的名称,一般建议采用Java变量的命名规则,实际上使用123d或者12,3d都是可以的,需要注意Spring版本的区别。Id只能定义一个名称,如果需要定义多个可以使用name属性进行定义,这里使用id、name名称都可以获取受管bean。针对获取受管bean对象的名称而言,其它的名称都叫做别名
bean实现类来源于第三方类库,因此无法在类中标注注解,通过xml配置方式较好;如果想用命名空间的配置则只能使用基于xml的配置
基本配置语法 <bean id=“标识符,不是必须的” name=“受管bean的名称” class="具体类的全名"scope=“应用范围”>

<bean class="java.util.Date"/> 对应的默认名称为java.util.Date#0

可以使用beanFactory.getBean(Date.class)获取受管bean,但是这是按照类型查找,不允许配置两个同
类型的。

<!-- 建议的配置方法,使用id进行唯一性标识,同样也可以使用name属性定义多个名称,不管是id或者
name定义的名称都不允许重复.name中可以使用空格、逗号和分号区分多个名称 -->
<bean id="now" name="/dd abc" class="java.util.Date" />

可以通过ApplicationContext接口中提供的方法遍历所有受管bean的名称

ApplicationContext ac=new
ClassPathXmlApplicationContext("com/yan/ioc01/applicationContext.xml");
String[] names = ac.getBeanDefinitionNames();
for(String tmp:names)
System.out.println(tmp);

同样如果一个受管bean有多个名称,可以获取受管bean的其它名称——别名

<bean id="123" name="/dd dd" class="java.util.Date"/>
ApplicationContext ac=new
ClassPathXmlApplicationContext("com/yan/ioc01/applicationContext.xml");
String[] dds = ac.getAliases("dd");
for(String tmp:dds)
System.out.println(tmp);

受管bean就是纳入到IoC/DI容器中,由容器负责创建和管理生命周期的javabean对象

<bean id=”受管bean的标识符,唯一,这里只能定义一个名称” class=”受管bean类的全名”
name=”别名,可以定义多个,使用逗号分隔”
scope=”范围,可以设置,默认singleton单例[Spring容器默认提供了单例模式的实现,不需要编
码],prototype原型,每次获取的都是新建的对象,另外还有和web相关的3个配置request、session、
global session”/>
  • Id命名建议采用Java变量的命名机制。
  • 受管bean的要求:一般来说任何java类都可以作为受管bean
  • class指向全类名,spring就是根据全类名,通过反射生成一个实例对象,id为这个实例对象的引用,所以必须在这个类中添加默认的构造方法,并且属性要有setter方法

scope属性用于定义生命周期

  • singleton默认值:单例对象,被标识为单例的对象在spring容器中只会存在一个实例
  • prototype多例原型对象,被标识为多例的对象每次在获得才会被创建,每次创建都是新的对象。对象的生命周期并不由框架负责维护
  • request用于Web环境下,对象与request生命周期一致
  • session用于Web环境下,对象与session生命周期一致
  • global session用于web环境下门户API应用开发中,对象的生命周期类似于Servlet API中的application
<bean id="user1" class="com.yan.ioc02.UserBean" scope="prototype"/>

绝大多数情况下,使用单例singleton默认值,但是在与struts2整合时,务必要用prototype多例,因为struts2在每次请求都会创建一个新的Action,若为单例,在多请求情况下,每个请求找找spring拿的都是同一个action

基于注解配置

bean的实现类是在当前项目开发的,可以直接在Java类中使用基于注解的配置

纯注解配置

针对受管bean可以在类上添加@Componenet,意思是组件。另外还有等价的三个注解@Controller控制器、@Service业务bean、@Repository生命DAO类型的受管bean

//默认名称为类名称,只是首字母小写;可以使用value属性定义一个受管bean的id值
//@Component
@Controller("useruser")
public class User {
}

配置

@Configuration
@ComponentScan("com.yan.ioc03") //设置自动扫描的包名称,该包或者子包下的所有添加了对
应注解的类会纳入到Spring容器中进行管理
public class JavaConfig {
}

编码

ApplicationContext ac=new
AnnotationConfigApplicationContext(JavaConfig.class);
User user=ac.getBean("useruser",User.class);
System.out.println(user);

默认单例,如果需要使用多实例方式@Scope(“prototype”)

@Controller("useruser")
@Scope("prototype")
public class User {
}

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"
xmlns:context="http://www.springframework.org/schema/context" 为了简化配置可以
引入一个新的名空间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.xsd">
<!-- 打开自动组件扫描,指定扫描的包名称为com.yan.ioc04 -->
<context:component-scan base-package="com.yan.ioc04"/>
</beans>

定义受管bean

@Component("aaa")
public class User {
}

编程调用

ApplicationContext ac=new
ClassPathXmlApplicationContext("com/yan/ioc04/applicationContext.xml");
User user=ac.getBean("aaa",User.class);
System.out.println(user);

基于JavaConfig配置

JavaConfig方式的优势在于可以通过代码方式控制bean初始化的整体逻辑,所以如果实例化bean的逻辑比较复杂时就比较适合使用JavaConfig类配置的方式

@Configuration //用于声明当前类是一个配置类
public class JavaConfig {
@Bean //用于声明当前方法的返回值是一个受管bean,默认方法名称就是受管bean的id值
public User myUser(){
User res= new User();
res.setId(99L);
res.setUsername("name_"+99);
return res;
}
}

编码调用

ApplicationContext ac=new
AnnotationConfigApplicationContext(JavaConfig.class);
User tmp=ac.getBean("myUser",User.class);
System.out.println(tmp);

多实例方式@Scope

@Configuration //用于声明当前类是一个配置类
public class JavaConfig {
@Scope("prototype")
@Bean //用于声明当前方法的返回值是一个受管bean
public User myUser(){
User res= new User();
res.setId(99L);
res.setUsername("name_"+99);
return res;
}
}

spring注入方式

spring的依赖注入分为构造器注入、setter注入和接口注入三种方式,构造器注入和setter注入是依赖注入的两种主要方式,构造器注入和setter注入都是通过java的反射技术得以实现的。最为常用的主要实际上有三种方式,分别是:构造方法注入、set方法注入和注解注入
接口注入

public class MyServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse
response) throws ServletException, IOException{
String name=request.getParameter("name");
}
}
设置器注入:set方式注入

要求:无参构造器,对应的set方法

  • 值类型用value注入
  • 引用类型用ref注入
<bean id="user" class="com.yan.di02.User">
<property name="id" value="#{now.year+1900}"/>
<property name="name" value="zhangsan"/>
<property name="birth" ref="now"/>
<property name="role" ref="role" />
</bean>
<bean id="role" class="com.yan.di02.Role"/>
<bean id="now" class="java.util.Date"/>

利用匿名受管bean定义简化配置

<bean id="user" class="com.yan.di02.User">
<property name="id" value="#{now.year+1900}"/>
<property name="name" value="zhangsan"/>
<property name="birth">
<bean id="now" class="java.util.Date"/>
</property>
<property name="role">
<bean class="com.yan.di02.Role"/>
</property>
</bean>

引入p名空间简化配置
添加名空间声明xmlns:p=“http://www.springframework.org/schema/p” ,没有对应的schema

<bean id="user" class="com.yan.di02.User" p:id="#{now.year+1900}"
p:name="zhangsan" p:birth-ref="now" p:role-ref="role"/>
<bean id="now" class="java.util.Date" p:month="8" p:date="8"/>
<bean id="role" class="com.yan.di02.Role" p:name="一般管理员"/>

特殊配置方法 [过去的面试题]

针对name属性设置一个具体的值"111"
<property name="name">
<value>111</value>
</property>
<property name="name"> 表示针对name=""
<value></value>
</property>
<property name="name"> 表示针对name=null
<null/>
</property>
针对应用类型的特殊配置方法
<property name="birth">
<ref bean="now"/>
</property>
<property name="role">
<bean class="com.yan.di02.Role"/>
</property>

构造器注入:构造方法注入

对应参数的构造器

<bean id="user" class="com.yan.di03.User">
<constructor-arg value="111"/>
<constructor-arg value="zhangsan"/>
<constructor-arg>
<bean class="java.util.Date"/>
</constructor-arg>
</bean>

对应的构造器,默认要求配置和参数顺序一致

public User(Long id,String name,Date birth){
this.id=id;
this.name=name;
this.birth=birth;
}
  • name属性:构造器参数名称
<bean id="user" class="com.yan.di03.User">
<constructor-arg value="zhangsan" name="name"/>
<constructor-arg value="111" name="id"/>
<constructor-arg ref="now"/>
</bean>
  • index属性:构造器的参数索引
<bean id="user" class="com.yan.di03.User">
<constructor-arg value="zhangsan" index="1"/>
<constructor-arg value="111" index="0"/>
<constructor-arg index="2">
<bean class="java.util.Date"/>
</constructor-arg>
</bean>
  • type属性:构造器的参数类型
<bean id="user" class="com.yan.di03.User">
<constructor-arg value="zhangsan" />
<constructor-arg value="111" type="java.lang.Long"/>
<constructor-arg index="2">
<bean class="java.util.Date"/>
</constructor-arg>
</bean>

可以引入c命名空间简化xml配置 xmlns:c=“http://www.springframework.org/schema/c”

<bean id="user" class="com.yan.di04.User2" c:_0="99" c:name="zhangsan"
c:birth-ref="now"/>
<bean id="now" class="java.util.Date"/>

总结

  • 从理论上说,使用构造器注入可以保证在对象创建完毕后则可以获取到一个完整可用的对象,官方推荐的
  • 但是从具体应用上说,使用设置器注入比较灵活,所以在具体应用中,这种方式使用较多
  • 在应用中可以考虑使用构造器注入必要内容,其它采用设置器补齐

要求一个用户对象中用户id永不为null

<bean id="user" class="com.yan.di04.User" p:name="zhangsan">
<constructor-arg value="111"/>
<property name="birth">
<bean class="java.util.Date"/>
</property>
</bean>

注解注入

通过@Autowired注解方式,可以实现自动装配,只要在对应的属性上面添加该注解进来,但是@Autowired注解是按照byType类型来注入。实际上注解注入也是设置器注入
推荐使用的方法:在set方法上添加注解

  • 值类型用@value注入
  • 引用类型用@Autowired注入
@Data
@Component("user")
public class User implements Serializable {
private Long id;
private String name;
private Date birth;
private Role role;
@Value("18") //其中可以使用SpEL定义对应的数据,例如#{1+2}或者#
{now.year+1900}。框架会自动进行数据类型转换,如果转换失败则报错
public void setId(Long id) {
this.id = id;
}
@Value("zhangsan")
public void setName(String name) {
this.name = name;
}
@Autowired //默认按照类型查找对应的注入受管bean对象
@Qualifier("now") //如果不使用按照类型查找对应的注入对象,则可以使用Qualifier指
定名称
public void setBirth(Date birth) {
this.birth = birth;
}
@Autowired
@Qualifier("role")
public void setRole(Role role) {
this.role = role;
}
}

对应的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"
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.xsd">
<context:component-scan base-package="com.yan.di01"/>
<bean id="now" class="java.util.Date"/>
</beans>

将所有的配置添加在set方法上,则认为可读性变差,所以也可以直接定义在属性上

@Data
@Component("user")
public class User implements Serializable {
@Value("#{now.year+1900}")
private Long id;
@Value("zhangsan")
private String name;
@Autowired
@Qualifier("now")
private Date birth;
@Autowired
@Qualifier("role")
private Role role;

不支持这个配置方法的人认为破坏了类的封装性
构造器的注解注入

@Component("user")
public class User2 implements Serializable {
@Value("888")
private Long id;
private String name;
private Date birth;
public User2(@Value("99") Long id, @Value("zhangsan") String
name,@Autowired @Qualifier("now") Date birth){
this.id=id;
this.name=name;
this.birth=birth;
}
// @Value("999")
public void setId(Long id) {
this.id = id;
}

执行流程 :构造器—属性—set方法

xml配置中的继承

<bean id="person" class="com.yan.ext.Person" p:name="zhangsan" p:birthref="now"/>
<bean id="student" parent="person" class="com.yan.ext.Student" p:id="99"
p:score="12.34"/>
<bean id="now" class="java.util.Date" p:year="1000"/>

模板的概念:抽象父类—不能获取父类对象

<bean id="student" parent="person" class="com.yan.ext.Student" p:id="99"
p:score="12.34" p:name="lisi"/>
<bean id="now" class="java.util.Date" p:year="1000"/>
<bean id="person" abstract="true" p:name="zhangsan" p:birth-ref="now"/> 在
IDEA工具中会有报错,但是直接运行不会有问题;
也可以修改为
<bean id="person" abstract="true">
<property name="name" value="zhangsan"/>
<property name="birth" ref="now"/>
</bean>

自动装配

Spring容器可以在不使用 和 元素的情况下自动装配相互协作的bean之间的关系,这有助于减少编写一个大的基于Spring的应用程序的XML配置的数量。
优点:

  • 自动装配能显著减少配置的数量。不过,采用bean模板也可以达到同样的目的
  • 自动装配可以使配置与 java 代码同步更新。如需要给一个java类增加一个依赖,那么该依赖将被自动实现而不需要修改配置。
    因此强烈推荐在开发过程中采用自动装配,而在系统趋于稳定的时候改为显式装配的方式
    缺点:
  • 尽管自动装配比显式装配更神奇,但是Spring会尽量避免在装配不明确的时候进行猜测,因为装配不明确可能出现难以预料的结果,而且Spring所管理的对象之间的关联关系也不再能清晰的进行文档化
  • 对于那些根据Spring配置文件生成文档的工具来说,自动装配将会使这些工具没法生成依赖信息
  • 自动装配可以减轻配置的工作量,但同时使得配置文件的可读性变得很差,因为不可能从配置文件中获知这些对象之间得依赖关系,从而维护困难!

自动装配有5种类型
1、no 是默认值,表示不使用autowiring。 必须显示的使用 <ref /> 标签明确地指定bean合作者,对于部署给予更大的控制和明了。
2、byName 根据属性名自动装配。此选项将检查容器并根据名字查找与属性完全一致的bean,并将其与属性自动装配。如在bean定义中将 autowire设置为by name,而该bean包含master属性(同时提供setMaster(…)方法),Spring就会查找名为master的bean定义,并用它来装配给master属性。
3、byType 如果容器中存在一个与指定属性类型相同的bean,那么将与该属性自动装配。如果存在多个该类型的bean,那么将会抛出异常,并指出不能使用byType方式进行自动装配。若没有找到相匹配的bean,则什么事都不发生,属性也不会被设置。如果不希望这样,那么可以通过设置 dependencycheck="objects"让Spring抛出异常。
4、constructor 与byType的方式类似,不同之处在于它应用于构造器参数。如果在容器中没有找到与构造器参数类型一致的bean,那么将会抛出异常。
5、default采用默认自动装配方法

<beans default-autowire="byType"> 全局配置
<bean id="aa" class="com.yan.test.A" autowire="default"/> 针对当前受管bean的自动
装配

注解@Autowired—注解开发中常用

  • 默认使用的是按照类型自动装配,如果有多个相同类型数据时再自动按照名称自动装配
  • 如果需要按照名称自动装配,则可以配合注解@Qualifier(“now”)
@Data
@Component("user")
public class User {
@Autowired
private String name;
@Autowired
private Date birth;
@Autowired
private Integer id;
}

xml文件配置

<bean id="birth" class="java.util.Date"/>
<bean id="name" class="java.lang.String">
<constructor-arg value="abde"/>
</bean>
<bean id="id" class="java.lang.Integer">
<constructor-arg value="123"/>
</bean>
<context:component-scan base-package="com.yan.auto02"/>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值