一、Bean种类
1、普通Bean
<bean id="" class="A"> ;spring直接创建A实例对象并且返回
2、特殊Bean
如果一个Bean实现了 FactoryBean接口,那他就是一个特殊的Bean,当spring实例化这个bean的时候,会调用 getObject() 方法,返回 getObject() 方法返回的值
这种特殊的Bean,我们一般称为FactoryBean,例如ProxyFactoryBean,表示生产proxy的Bean
<bean id="" class="proxyFactoryBean">,获得的是proxyBean。(getObject()方法返回的是proxy)
补充:
FactoryBean:具有工厂生产对象的能力,但是只能生产特定的对象(典型例子SqlSessionFactoryBean),目的就是隐藏复杂的bean的配置
//举一个例子
public class UserFactoryBean implements FactoryBean<User> {
@Override
public User getObject() throws Exception {
//进行复杂的配置
User user = new User();
return user;
}
@Override
public Class<?> getObjectType() {
return null;
}
@Override
public boolean isSingleton() {
return false;
}
}
BeanFactory:生产Bean的工厂,可以生产任意Bean。
二、基于XML装配Bean
1、实例化方式
1.1默认构造
<bean id="" class=""> //必须提供构造方法
1.2静态工厂
facatory.class
public class ServiceFactory {
public static Service createService(){
return new ServiceImpl();
}
}
service.class
public interface Service {
public void save();
}
serviceImpl.class
public class SaveDaoImpl implements SaveDao {
public void save(){
System.out.println("保存完毕");
}
}
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">
<!--createService 生产对象的方法-->
<bean id="serviceFactory" class="com.test.ServiceFactory" factory-method="createService"></bean>
</beans>
测试
public class Test {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-context.xml");
Service service = applicationContext.getBean("serviceFactory",Service.class); //Service.class相当于强转了
service.save();
}
}
1.3实例工厂
factroy.class
public class ServiceFactory {
public Service createService(){
return new ServiceImpl();
}
}
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">
<bean id="serviceFactory" class="com.test.ServiceFactory"></bean>
<bean id="userService" factory-bean="serviceFactory" factory-method="createService"></bean> //指定工厂和方法
</beans>
测试
public class Test {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-context.xml");
ServiceImpl service = applicationContext.getBean("userService",ServiceImpl.class);
service.save();
}
}
2、作用域
用于确定spring创建bean的个数
<?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">
<bean id="userService" class="com.test.ServiceImpl" scope="singleton"></bean> //singleton单例默认,只存在一个bean,(默认),prototype:多例
</beans>
测试
public class Test{
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-context.xml");
ServiceImpl service = applicationContext.getBean("userService",ServiceImpl.class);
ServiceImpl service2 = applicationContext.getBean("userService",ServiceImpl.class);
System.out.println(service);
System.out.println(service2);
}
}
3、生命周期
3.1初始化和销毁
目标bean
public class ServiceImpl implements Service {
public void save(){
System.out.println("save");
}
public void init(){
System.out.println("初始化");
}
public void destroy(){
System.out.println("销毁");
}
}
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">
<bean id="userService" class="com.test.ServiceImpl" init-method="init" destroy-method="destroy"></bean> //配值初始化和销毁方法
</beans>
测试
public class Test{
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-context.xml");
ServiceImpl service = applicationContext.getBean("userService",ServiceImpl.class);
System.out.println(service);
applicationContext.getClass().getMethod("close").invoke(applicationContext); //当容器关闭了,才会调用destory方法
//如果不用反射
//ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-context.xml");
//ServiceImpl service = applicationContext.getBean("userService",ServiceImpl.class);
//System.out.println(service);
//applicationContext.close();
}
}
要求:该bean需要是单例模式和容器需要调用close方法,destory方法才会被执行。
3.2BeanPostProcessor(后处理bean)
容器中所有的 bean 被初始化前后会调用者两个方法
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MyBeanPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("before:"+beanName);
return bean;
}
public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
System.out.println("after:"+beanName);
return Proxy.newProxyInstance( //如果需要对执行的方法进行特殊的处理,例如写入日志等等。如果没有特别需求,可以直接 return bean;
MyBeanPostProcessor.class.getClassLoader(),
bean.getClass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开启事务");
//执行目标代码
Object invoke = method.invoke(bean, args);
System.out.println("结束事务");
return invoke;
}
});
}
}
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">
<bean id="userService" class="com.test.ServiceImpl" init-method="init" destroy-method="destroy"></bean> //查看之前写的ServiceImpl
<bean class="com.test.MyBeanPostProcessor"></bean> //只需要添加一个class就可以监听bean的初始化了
</beans>
测试
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.lang.reflect.InvocationTargetException;
public class Test{
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-context.xml");
Service service = applicationContext.getBean("userService",Service.class); //Service.class,不能是ServiceImpl.class
service.save();
applicationContext.getClass().getMethod("close").invoke(applicationContext); //当容器关闭了,才会调用destory方法
}
}
3.3属性依赖注入
3.3.1构造方法注入
public class User {
private Integer uid;
private String username;
private Integer age;
public User(Integer uid, String username) {
super();
this.uid = uid;
this.username = username;
}
public User(String username, Integer age) {
super();
this.username = username;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"uid=" + uid +
", username='" + username + '\'' +
", age=" + age +
'}';
}
}
xml
<bean id="user" class="com.test.User">
<constructor-arg index="0" type="java.lang.String" value="1"></constructor-arg>
<constructor-arg index="1" type="java.lang.Integer" value="2"></constructor-arg>
</bean>
测试
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-context.xml");
User user = (User) applicationContext.getBean("user");
System.out.println(user);
3.3.2setter方法注入
public class Person {
public String name;
public Address addr;
public Address getAddr() {
return addr;
}
public void setAddr(Address addr) {
this.addr = addr;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", addr=" + addr +
'}';
}
}
public class Address {
private String addr;
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
@Override
public String toString() {
return "Address{" +
"addr='" + addr + '\'' +
'}';
}
}
xml
<bean id="user" class="com.test.User">
<constructor-arg index="0" type="java.lang.String" value="1"></constructor-arg>
<constructor-arg index="1" type="java.lang.Integer" value="2"></constructor-arg>
</bean>
<bean id="person" class="com.test.Person">
<property name="name" value="小明"></property>
<!-- <property name="name">-->
<!-- <value>小明</value>-->
<!-- </property>-->
<property name="addr" ref="homeAddrId"></property>
<!-- <property name="addr">-->
<!-- <ref bean="homeAddrId"/>-->
<!-- </property>-->
</bean>
<bean id="homeAddrId" class="com.test.Address">
<property name="addr" value="北京"></property>
</bean>
补充
SpEL[了解]
对<property>进行统一编程,所有的内容都使用value
格式:<property name="" value="#{表达式}">
#{123}、#{'jack'} : 数字、字符串
#{beanId} :另一个bean引用
#{beanId.propName} :操作数据
#{beanId.toString()} :执行方法
#{T(类).字段|方法} :静态方法或字段
<!--
<property name="cname" value="#{'jack'}"></property>
<property name="cname" value="#{customerId.cname.toUpperCase()}"></property> 通过另一个bean,获得属性,调用的方法
<property name="cname" value="#{customerId.cname?.toUpperCase()}"></property> ?. 如果对象不为null,将调用方法
-->
<bean id="customerId" class="com.itheima.f_xml.d_spel.Customer">
<property name="cname" value="#{customerId.cname?.toUpperCase()}"></property> //?:表示如果是空,就不转换大写
<property name="pi" value="#{T(java.lang.Math).PI}"></property>
</bean>
3.3.3 集合注入
public class CollData {
private String[] array;
private List<Integer> list;
private Set<String> set;
private Map<String,String> map;
private Properties properties;
public void setMap(Map<String, String> map) {
this.map = map;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
public void setSet(Set<String> set) {
this.set = set;
}
public void setList(List<Integer> list) {
this.list = list;
}
public void setArray(String[] array) {
this.array = array;
}
@Override
public String toString() {
return "CollData{" +
"array=" + Arrays.toString(array) +
", list=" + list +
", set=" + set +
", map=" + map +
", properties=" + properties +
'}';
}
}
xml
<bean id="collData" class="com.test.CollData">
<property name="array">
<array>
<value>sdf</value>
<value>sdf</value>
</array>
</property>
<property name="list">
<list>
<value>1</value>
<value>2</value>
<value>3</value>
</list>
</property>
<property name="set">
<set>
<value>sdf</value>
<value>sdf</value>
</set>
</property>
<property name="map">
<map>
<entry key="name" value="小明"></entry>
<entry>
<key><value>age</value></key>
<value>10</value>
</entry>
</map>
</property>
<property name="properties">
<props>
<prop key="name">小明</prop>
<prop key="age">10</prop>
</props>
</property>
</bean>
三、基于注解装配Bean
在之前
需要生成一个spring-context.xml,来配置bean
<?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="springContext" class="com.zy.myshop.comment.until.MyApplicationContext" />
<bean id="getuserImpl" class="com.zy.myshop.dao.impl.GetUserImpl"></bean>
<bean id="userLoginimpl" class="com.zy.myshop.service.impl.UserLoginImpl"></bean>
</beans>
对于 DI 使用注解,将不再需要在 Spring 配置文件中声明 Bean 实例。Spring 中使用注解, 需要在原有 Spring 运行环境基础上再做一些改变
需要在 Spring 配置文件中配置组件扫描器,用于在指定的基本包中扫描注解。
<?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">
//<bean id="springContext" class="com.zy.myshop.comment.until.MyApplicationContext" />
<context:annotation-config /> //xml和注解混和使用,就是如果bean用xml配置,依赖注入使用注解的话,如果不加上这句,会出现注解不生效
<context:component-scan base-package="com.zy.myshop"/> //组件扫描,扫描含有注解的类
</beans>
说明:schema命名空间
命名空间声明
默认:xmlns=""
显示:xmlns:别名="",例如xmlns:context
确定schema xsd 文件位置
xsi:schemaLocation="名称 位置 名称2 位置2......."(成对)
3.1 注解(解放xml装配bean):
@Component: 需要在类上使用注解 @Component
,该注解的 value 属性用于指定该 bean 的 id 值。(像一般工具类等的注解)
取代<bean class=""> @Compose("id") 取代<bean id="" class="">
下面是web开发@component衍生注解(功能一样)
@Repository:
用于对 DAO 实现类进行注解 (数据访问层)
@Service :
用于对 Service 实现类进行注解 (逻辑业务层)
@Controller
: 用于对 Controller 实现类进行注解 (视图层)
@RestController
: 用于对 Controller 实现类进行注解,不再返回视图,全部必须返回json数据,不需要再加上@ResponseBody注解(表示返回的数据为json格式)
3.2 依赖注入(给bean添加属性)
3.2.1 普通值
@Value
需要在属性上使用注解 @Value
,该注解的 value 属性用于指定要注入的值。
application.xml配置,需要扫描到配置文件
<context:property-placeholder location="classpath:sms.properties"></context:property-placeholder>
3.2.2 引用值
方式1:按类型注入
@Autowired
前提:一个接口只有一个实现类
需要在域属性上使用注解 @Autowired
,该注解默认使用 按类型自动装配 Bean 的方式。
使用该注解完成属性注入时,类中无需 setter。当然,若属性有 setter,则也可将其加到 setter 上。
方式2:按名称注入
@Autowired
@Qualifier("名称")
方式3:按名称注入
@Resource("名称")
需要在域属性上使用注解 @Resource
,该注解有一个 name
属性,可以创建指定的 bean
@Resource(name = "userService")
private UserService userService;
3.2.3作用域
@Scope
需要在类上使用注解 @Scope
,其 value 属性用于指定作用域。默认为 singleton。
3.2.4初始化和销毁
@PostConstruct
在方法上使用 @PostConstruct
相当于初始化
@PostConstruct
public void myInit(){
System.out.println("初始化");
}
@PreDestroy
public void myDestory(){
System.out.println("销毁");
}
3.3基本使用(示例)
注意此时有一个spring-aop包很重要,maven中依赖的sprint-context包包含了spring-aop包
dao
public interface StudentDao {
void save();
}
daoImpl
@Repository("studentDao")
public class StudentDaoImpl implements StudentDao {
@Override
public void save() {
System.out.println("保存数据");
}
}
service
public interface StudentService {
public void save();
}
serviceImpl
@Service
public class StudentServiceImpl implements StudentService{
@Autowired
@Qualifier(value = "studentDao") //按名字注入(Dao必须指定名字)
//@Resource(name = "studentDao") //这一句可以代替上面两句
private StudentDao dao;
@Override
public void save() {
System.out.println("调用dao");
dao.save();
}
}
controller
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Controller(value = "studentController")
public class StudentController{
@Autowired //按照类型注入
private StudentService service;
public void save(){
System.out.println("调用service");
service.save();
}
@PostConstruct
public void myInit(){
System.out.println("初始化");
}
@PreDestroy
public void myDestory(){
System.out.println("销毁");
}
}
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.test"></context:component-scan>
</beans>
测试
public class Test{
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-context.xml");
StudentController StudentController = ((StudentController) applicationContext.getBean("studentController"));
StudentController.save();
applicationContext.getClass().getMethod("close").invoke(applicationContext);
}
}
3.4 补充
MyapplicationContext
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.ClassPathXmlApplicationContext;
//public class MyApplicationContext {
// public static ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-context.xml");;
//}
public class MyApplicationContext implements ApplicationContextAware, DisposableBean {
public static final Logger logger = LoggerFactory.getLogger(MyApplicationContext.class);
private static ApplicationContext applicationContext;
public static ApplicationContext getApplicationContext() {
assertContextInjected();
return applicationContext;
}
public void destroy() throws Exception {
logger.debug("清空 applicationContext");
System.out.println("清空application.....");
applicationContext = null;
}
public static <T> T getBean(String name) {
assertContextInjected();
logger.info("getbean");
System.out.println("getBean........");
return (T) applicationContext.getBean(name);
}
public static <T> T getBean(Class<T> clazz) {
assertContextInjected();
return applicationContext.getBean(clazz);
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
logger.info("加载 application");
System.out.println("加载application");
MyApplicationContext.applicationContext = applicationContext;
}
private static void assertContextInjected() {
Validate.validState(applicationContext != null, "applicationContext 属性未注入,请在 spring-context.xml 配置中定义 SpringContext");
}
}
在托管的类上使用注解
注解与 XML 配置的区别
注解的好处是,配置方便,直观。但其弊端也显而易见:以硬编码的方式写入到了 Java 代码中,其修改是需要重新编译代码的。
XML 配置方式的最大好处是,对其所做修改,无需编译代码,只需重启服务器即可将新的配置加载。
若注解与 XML 同用,XML 的优先级要高于注解。这样做的好处是,需要对某个 Bean 做修改,只需修改配置文件即可。
来源:oschina
链接:https://my.oschina.net/u/4367553/blog/3545994