Spring学习记录(二):Spring IOC学习

Spring学习记录(二):Spring IOC学习

提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

       上一篇概括了Spring的概念问题、优势以及框架结构,现在正式进入IOC的整理。


一、IOC是什么?

       控制反装不是一种技术,而是设计思想。他的目的很明确,为设计出更加松耦合的程序。其中控制指的是程序中对象的控制权限,反转指的是对象的控制权由原来的开发者转移到Spring容器控制。

二、IOC容器的自定义

1.引入库

<dependencies>
        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>
        <dependency>
            <groupId>jaxen</groupId>
            <artifactId>jaxen</artifactId>
            <version>1.1.6</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

2.传统方法编写

       编写Dao层代码:

public interface UserDao {
    void save();
}
public class UserDaoImpl implements UserDao {
    @Override
    public void save() {
        System.out.println("保存成功");
    }
}

       编写service层:

public interface UserService {
    void save();
}
public class UserServiceImpl implements UserService {
    @Override
    public void save() {
        UserDao userDao = new UserDaoImpl();
        userDao.save();
    }
}

       编写测试代码:

public class SpringTest {
    @Test
    public void test1(){
        UserService userService=new UserServiceImpl();
        userService.save();
    }
}

       我们可以看到,传统方式调用存在编译期依赖,耦合过高,而且每次new对象会造成服务器压力过大,我们最重要的就是去掉这个new对象的过程完成解耦合。

3.改造传统方法,降低耦合度(自定义IOC容器)

       使用反射去掉new关键字:

public class UserServiceImpl implements UserService {
    @Override
    public void save() throws ClassNotFoundException, InstantiationException, IllegalAccessException {

        //UserDao userDao = new UserDaoImpl();
        //使用反射
        UserDao userDao = (UserDao) Class.forName("com.ssm.dao.impl.UserDaoImpl").newInstance();
        userDao.save();
    }
}

在编译期就算没有Dao也不会报错,解决编译期依赖问题。但是存在硬编码问题。改造思路:反射+配置文件。
       改造步骤分析:
              1.准备一个配置文件:beans.xml

<beans>
    <!--id:标识   class:存的全路径-->
    <bean id="UserDao" class="com.ssm.dao.impl.UserDaoImpl"></bean>
</beans>

              2.编写一个工厂工具类,工厂类使用dom4j来解析配置文件,获取到类全路径。
              3.使用反射生成对应类的实例对象,存到map中(IOC容器)。

public class BeanFactory {

    private static Map<String, Object> iocMap = new HashMap<>();

    //程序启动时,初始化对象实例
    static {
        //1.读取配置文件
        InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
        //2.解析xml(使用dom4j)
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(resourceAsStream);
            //3.编写xpath表达式
            String xpath = "//bean";
            //4.获取到所有bean标签
            List<Element> list = document.selectNodes(xpath);
            //5.遍历并使用反射创建对象实例,存到map中(充当IOC容器)
            for (Element element : list) {
                String id = element.attributeValue("id");
                String className = element.attributeValue("class");
                //6.使用反射生成实例对象
                Object o = Class.forName(className).newInstance();
                //7.存入map中 key:id value:o
                iocMap.put(id, o);
            }
        } catch (DocumentException | ClassNotFoundException | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
    public static Object getBean(String beanId){
        return iocMap.get(beanId);
    }
}

              4.调用。

public class UserServiceImpl implements UserService {
    @Override
    public void save() throws ClassNotFoundException, InstantiationException, IllegalAccessException {

        //UserDao userDao = new UserDaoImpl();
        //使用反射
        //UserDao userDao = (UserDao) Class.forName("com.ssm.dao.impl.UserDaoImpl").newInstance();
        //利用工厂类进行获取
        UserDao userDao = (UserDao)BeanFactory.getBean("userDao");
        userDao.save();
    }
}

4.总结

      BeanFactory就是一个简单的IOC容器,之前我们需要一个UserDao实例来new对象,现在我们只需要交给Bean工厂(IOC)控制,达到解耦合的目的。

三、利用Spring进行快速入门

1.引入库

<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

2.编写核心配置文件

      依旧利用上文的Dao形式,创建配置文件applicationContext.xml(不要忘记约束头)

<bean id="UserDao" class="com.ssm.dao.impl.UserDaoImpl"></bean>

3.利用SpringAPI获取

public class SpringTest {
    @Test
    public void test1(){
        //获取spring上下文对象,借助上下文对象可以获取到IOC容器中的bean对象
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
        //使用上下文对象,从IOC容器中获取Bean对象
        UserDao userDao = (UserDao) applicationContext.getBean("UserDao");
        userDao.sava();
    }
}

四、Spring相关API

      主要关注BeanFactory和ApplicationContext
在这里插入图片描述

1.BeanFactory

      BeanFactory是IOC容器的核心接口,定义了IOC基本功能。

public void test2(){
        //核心接口,但是不会创建bean对象存入容器中
        BeanFactory xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
        //在getBean的时候才真正创建bean对象
        UserDao userDao = (UserDao)xmlBeanFactory.getBean("UserDao");
        userDao.sava();
    }

2.ApplicationContext

     代表应用上下文对象,获得IOC容器的Bean对象。
     特点:启动时就创建并加载所有实例(和BeanFactory的不同之处)。
     常用实现类:

//当使用注解配置容器对象时,创建Spring容器读取注释
 AnnotationConfigApplicationContext
//从磁盘路径加载配置文件
FileSystemXmlApplicationContext
//从类的根路径加载配置文件
ClassPathXmlApplicationContext

     常用的getBean有三个:

//根据Bean的id从容器中获得Bean实例,需要强转
Object getBean(String var1) throws BeansException;
//根据类型从容器中获取Bean实例,当有多个相同类型时,会报错
<T> T getBean(Class<T> var1) throws BeansException;
//根据Bean的id和类型获取Bean实例,结合上面两种方法
<T> T getBean(String var1, Class<T> var2) throws BeansException;

PS:getBean方法来自BeanFactory接口。

五、Spring配置文件

1.基本配置

<bean id="" class="" scope=""></bean>
#默认调用类中的无参构造
取值范围说明
singleton默认值,单例的
prototype多例的
requestWEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中
sessionWEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中
global sessionWEB 项目中,应用在 Portlet 环境,如果没有 Portlet 环境那么globalSession 相当于 session

2.Bean的生命周期

<bean id="" class="" scope="" init-method="" destroy-method=""></bean>

init-method=""      初始化。
destroy-method=""     销毁时。

六、Bean实例化三种方式

无参构造实例化
工厂静态方法实例化
工厂普通方法实例化

1.工厂静态方法实例化

   假设依赖jar包中有个A类,A类中有静态方法a1,其返回值是一个B对象,如果我们频繁调用B对象,那我们可以将创建权交给SpringIOC进行处理。

public class StaticFactoryBean {
    static UserDao createUserDao(){
        return new UserDaoImpl();
    }
}
<bean id="userDao" class="com.ssm.dao.StaticFactoryBean" factory-method="createUserDao"></bean>

2.工厂普通方法实例化

   假设依赖jar包中有个A类,A类中有普通方法a1,其返回值是一个B对象,如果我们频繁调用B对象,那我们可以将创建权交给SpringIOC进行处理。

public class DynamicFactoryBean {
    public UserDao createUserDao() {
        return new UserDaoImpl();
    }
}
<bean id="dynamicFactoryBean" class="com.ssm.dao.DynamicFactoryBean"></bean>
<bean id="userDao" factory-bean="dynamicFactoryBean" factory-method="createUserDao"></bean>

七、Bean依赖注入

1.依赖注入概述

   他是SpringIOC的具体实现,通过控制反转后,通过框架把持久层对象传入业务层,而不用我们自己去获取。

2.Bean依赖注入的方式

2.1 构造方法

   在UserServiceImpl中创建有参构造

public class UserServiceImpl implements UserService {

    private UserDao userDao;

    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }
    
    @Override
    public void save() {
        userDao.sava();
    }
}

   配置xml

<bean id="UserDao" class="com.ssm.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.ssm.service.impl.UserServiceImpl">
	<constructor-arg index="0" type="com.ssm.dao.UserDao" ref="UserDao"></constructor-arg>
</bean>

   测试

 @Test
    public void test3(){
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = (UserService)applicationContext.getBean("userService");
        userService.save();
    }

2.1 set方法(比较常用)

   在UserServiceImpl中创建有参构造(把有参构造删除)

public class UserServiceImpl implements UserService {

    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void save() {
        userDao.sava();
    }
}

   配置xml

<bean id="UserDao" class="com.ssm.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.ssm.service.impl.UserServiceImpl">
	<property name="userDao" ref="UserDao"></property>
</bean>

   测试

 @Test
    public void test3(){
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = (UserService)applicationContext.getBean("userService");
        userService.save();
    }

3.Bean依赖注入的数据类型

3.1 普通数据类型

   改造一下UserDaoImpl

 public class UserDaoImpl implements UserDao {

    private String username;
    private String password;

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public void sava() {
    	System.out.println(username);
        System.out.println(password);
        System.out.println("保存成功");
    }
}

   配置xml

<bean id="UserDao" class="com.ssm.dao.impl.UserDaoImpl">
        <property name="username" value="admin"></property>
        <property name="password" value="123456"></property>
    </bean>
    <bean id="userService" class="com.ssm.service.impl.UserServiceImpl">
        <property name="userDao" ref="UserDao"></property>
    </bean>

测试一下~

3.2 集合数据类型

   利用上述普通类型注入创建一个User对象并注入容器(练习一下)
   改造一下UserDaoImpl

public class UserDaoImpl implements UserDao {

    private List<Object> list;
    private Set<Object> set;
    private Object[] array;
    private Map<String,Object> map;
    private Properties properties;

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    public void setMap(Map<String, Object> map) {
        this.map = map;
    }

    public void setArray(Object[] array) {
        this.array = array;
    }

    public void setSet(Set<Object> set) {
        this.set = set;
    }

    public void setList(List<Object> list) {
        this.list = list;
    }

    @Override
    public void sava() {
        System.out.println(list);
        System.out.println(set);
        System.out.println(Arrays.toString(array));
        System.out.println(map);
        System.out.println(properties);
        System.out.println("保存成功");
    }


}

   配置xml

<bean id="User" class="com.ssm.damain.User">
        <property name="username" value="admin"></property>
        <property name="password" value="123456"></property>
    </bean>

    <bean id="UserDao" class="com.ssm.dao.impl.UserDaoImpl">
        <property name="list">
            <list>
                <value>simon</value>
                <ref bean="User"></ref>
            </list>
        </property>
        <property name="set">
            <set>
                <value>simon2</value>
                <ref bean="User"></ref>
            </set>
        </property>
        <property name="array">
            <array>
                <value>simon3</value>
                <ref bean="User"></ref>
            </array>
        </property>
        <property name="map">
            <map>
                <entry key="1" value="simon4"></entry>
                <entry key="2" value-ref="User"></entry>
            </map>
        </property>
        <property name="properties">
            <props>
                <prop key="k1">v1</prop>
                <prop key="k2">v2</prop>
            </props>
        </property>
    </bean>
    <bean id="userService" class="com.ssm.service.impl.UserServiceImpl">
        <property name="userDao" ref="UserDao"></property>
    </bean>

测试一下~

八、Spring注解开发

1.常用注解介绍(IOC)

注解说明
@Component使用在类上用于实例化Bean
@Controller对应表现层的Bean
@ Service对应的是业务层Bean
@ Repository对应数据访问层Bean
@Autowired使用在字段上根据类型进行依赖注入

   只要根据对应业务层中加入相关注解即可代替xml中的相关bean标签,如Service层改写:

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void save() {
        userDao.sava();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值