Spring框架-IOC

Spring框架-IOC,第1张

Spring框架-IOC 一、概述 1. Spring介绍

Spring:分层的 Java SE/EE应用 full-stack 轻量级开源框架,以 IoC(Inverse Of Control:反转控制)和AOP(Aspect Oriented Programming:面向切面编程)为内核。

  • 提供了展现层 SpringMVC 和持久层 Spring JDBCTemplate 以及业务层事务管理等众多的企业级应用技术
  • 可以整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的Java EE 企业应用开源框架。

优势

  • 方便解耦,简化开发:通过 Spring 提供的 IoC容器,可以将对象间的依赖关系交由 Spring 进行控制,避免硬编码所造成的过度耦合。
    用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
  • AOP 编程的支持:通过 Spring的 AOP 功能,方便进行面向切面编程,许多不容易用传统 OOP 实现的功能可以通过 AOP 轻松实现。
  • 方便集成各种优秀框架
    Spring对各种优秀框架(Struts、Hibernate、Hessian、Quartz等)的支持。
  • 降低 JavaEE API 的使用难度
    Spring对 JavaEE API(如 JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些API 的使用难度大为降低。
  • 声明式事务的支持
    通过声明式方式灵活的进行事务管理,提高开发效率和质量。

体系结构

2. IOC介绍

IOC(inversion of control):控制反转。对象的使用者不是创建者,将对象的创建反转给spring框架来创建和管理。

原理

平时在servlet中调用service中的方法时,需要通过new的方式创建service相关的对象,然后调用该对象的方法。

现在有了Spring,就无需通过new来创建这个对象。只需要通过XML或注解的方式将需要的类配置到Spring中,Spring会通过反射机制在Spring容器中创建和维护该类的对象。使用时直接通过调用Spring提供的方法来获取该对象来使用即可

作用

避免了类和类之间通过硬编码的方式耦合在一起

3. 相关API 3.1 ApplicationContext接口

实现类

  • ClassPathXmlApplicationContext:从类的根路径下加载配置文件推荐使用
  • FileSystemXmlApplicationContext:从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
  • AnnotationConfigApplicationContext:当使用注解配置容器对象时,需要使用此类来创建 spring 容器。用来读取注解。
3.2 getBean()方法

getBean(String name):通过传入配置文件中实例id来获取实例

public Object getBean(String name) throws BeansException {
    assertBeanFactoryActive();
    return getBeanFactory().getBean(name);
}
UserService userService1 = (UserService)applicationContext.getBean("userService");

getBean(Class requiredType):通过传入类型名来获取实例

注意:当容器中相同类型的Bean有多个时,则此方法会报错。

public  T getBean(Class requiredType) throws BeansException {
    assertBeanFactoryActive();
    return getBeanFactory().getBean(requiredType);
}
UserService userService2 = applicationContext.getBean(UserService.class);
二、XML配置 1. bean配置自定义Bean

作用:将配置的对象交由Spring 来创建。

1.1 基本配置

默认情况下它调用的是类中的无参构造函数,如果没有无参构造函数则不能创建成功

id属性:Bean实例在Spring容器中的唯一标识

class属性:Bean的全限定名称

public interface UserDao {
    void userDaoSave();
}

public class UserDaoImpl implements UserDao {
    public UserDaoImpl() {
        System.out.println("UserDaoImpl 构造方法");
    }

    @Override
    public void userDaoSave() {
        System.out.println("userDaoSave method...");
    }
}


        
	


1.2 实例个数

singleton:默认值

  • 实例化个数:单例
  • 实例创建时机:加载配置文件时
  • 实例生命周期:只要容器存在就一直活着

prototype:

  • 实例化个数:多例
  • 实例创建时机:获取对象时
  • 实例生命周期:同new出的对象一样,一直不用就会被垃圾回收

1.3 初始化和销毁

构造对象后会调用指定初始化方法,销毁对象之前会调用销毁方法

init-method:指定类中的初始化方法名称

destory-method:指定类中销毁方法名称

public class UserServiceImpl implements UserService {

    private String name;
    private int age;
    
    public void setName(String name) {
        this.name = name;
    }
    public void setAge(int age) {
        this.age = age;
    }
    
    //初始化-销毁方法
    public void init() {sout("init method...");}
    public void destory() {sout("destory method...");}
}

1.4 实例化的方式

无参构造方式


工厂静态方法

public class UserServiceFactory {
    public static UserService userServiceFactory() {
        return new UserServiceImpl();
    }
}

工厂实例方法

public class UserServiceFactory {
    public UserService userService() {
        return new UserServiceImpl();
    }
}


1.5 案例
  1. 导入Maven依赖

    
    	5.0.5.RELEASE
    
    
    
        
        
            org.springframework
            spring-context
            5.0.5.RELEASE
        
        
            junit
            junit
            4.12
        
    
    
  2. 创建接口和Bean实现类

    public interface UserDao {
        void userDaoSave();
    }
    
    public class UserDaoImpl implements UserDao {
        @Override
        public void userDaoSave() {
            System.out.println("userDaoSave method...");
        }
    }
    
    public interface UserService {
        void save();
    }
    
    public class UserServiceImpl implements UserService {
    	private UserDao userDao;
        
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
        
        @Override
        public void save() {
            userDao.save();
        }
    }
    
  3. 创建Spring配置文件并配置

    
    
    	
        
            
        
        	
            
        
    
    
  4. 加载配置文件,并获取Spring容器中的对象

    public class TestSpring {
        @Test
        public void testSpring() {
            //加载配置文件,获取应用上下文
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
            //获取容器中的对象
            UserService userService = (UserService) applicationContext.getBean("userService_id");
            //调用方法
            userService.save();
        }
    }
    
2. bean配置非自定义Bean

将非自定义的Bean加载到Spring容器中实现解耦,方式同自定义Bean方式相同,只是需要找准非自定义Bean的全限定名

2.1 编码案例

通过C3P0连接池获取数据库连接

  1. 导入Maven依赖

    
        
            mysql
            mysql-connector-java
            5.1.32
        
        
            c3p0
            c3p0
            0.9.1.2
        
        
            junit
            junit
            4.12
        
        
            org.springframework
            spring-context
            5.0.5.RELEASE
        
    
    
  2. 创建jdbc配置文件

    jdbc.driver = com.mysql.jdbc.Driver
    jdbc.url = jdbc:mysql://localhost:3306/test
    jdbc.username = root
    jdbc.password = 123456
    
  3. 调用c3p0的API获取连接

    @Test 
    //手动创建c3p0数据源 配置文件形式
    public void testC3P0() throws Exception {
        //读取配置文件
        ResourceBundle rb = ResourceBundle.getBundle("jdbc");
        String driver = rb.getString("jdbc.driver");
        String url = rb.getString("jdbc.url");
        String username = rb.getString("jdbc.username");
        String passwd = rb.getString("jdbc.password");
        //创建数据源对象
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setDriverClass(driver);
        dataSource.setJdbcUrl(url);
        dataSource.setUser(username);
        dataSource.setPassword(passwd);
        //获取连接
        Connection connection = dataSource.getConnection();
        //使用连接
        System.out.println(connection);
        //归还连接
        connection.close();
    }
    
2.2 创建xml文件

将c3p0的实例加载到Spring容器中,用Spring去创建对象,而非自己去new




    
        
        
        
        
    

2.3 加载properties

将数据库的配置抽取到properties文件中,并将properties文件中的数据也加载到Spring容器中,实现xml文件与properties文件间的解耦

  1. 引入context命名空间

    
    
    
    
    
  2. 加载properties到容器

     
    
  3. 通过Spring_SPL获取加载到Spring的properties值

    
    
    
    
    

实例




	
	
    
    

        
        
        
        
        
    

3. 依赖注入

注入依赖:实际就是通过配置的方式调用有参构造或set方法,对Spring容器中实例的属性进行赋值。以此来解除耦合,避免通过硬编码的方式直接调用实例的set方法或构造方法增加耦合度。

2.1 注入方式

对Spring容器中UserServiceImpl类的实例中的userDao属性进行赋值有两种方式:一是有参构造,二是对应的set方法。

public class UserDaoImpl implements UserDao {
    @Override
    public void userDaoSave() {
        System.out.println("userDaoSave method...");
    }
}

public class UserServiceImpl implements UserService {
    private UserDao userDao;
    
    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }
    
    public void setUserDao(UserDao userdao) {
        this.userDao = userdao;
    }
    
    @Override
    public void save() {
        userDao.userDaoSave();
    }
}
  • 有参构造

    
    
    
        
        
    
    
  • set方法

    
    
    
        
        
    
    
2.2 注入数据类型

对Spring容器中UserServiceImpl类的实例中的属性有基本类型、Bean、集合等不同的类型,对不同的类型有各自的注入方式

public class UserServiceImpl implements UserService {
    private String name;
    private int age;
    private List stringList;
    private Map userMap;
    private Properties properties;
    private UserDao userDao;
    
    public UserServiceImpl() {}
    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }

    public void setName(String name) {
        this.name = name;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public void setStringList(List stringList) {
        this.stringList = stringList;
    }
    public void setUserMap(Map userMap) {
        this.userMap = userMap;
    }
    public void setProperties(Properties properties) {
        this.properties = properties;
    }
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    
    @Override
    public void save() {
		sout("save...")
    }
}

  • 基本类型:value属性

    
            
            
    
    
  • Bean对象:ref属性

    
    
    
        
        
    
    
  • 配置文件:props标签

    
        
            
                1
                北京
            
        
    
    
  • 集合类型:不同的集合对应不同的标签

    
        
        
    
    
        
        
    
    
    
    
        
        	
            						
                aaa		
                bbb
                ccc
            
        
        
        
            
                
                
            
        
    
    
4. import标签

作用:由于项目中的Spring配置文件内容过于繁琐,因此可以对配置文件进行自定义拆分,通过import标签就可以实现引入其它文件内容

appContext-dao.xml




    
    

appContext-domain.xml




    
        
        
    
    
        
        
    

appContext-service.xml 引入其它配置文件








        
        

        
            
                
                
            
        
    

三、注解配置

Spring的注解可以简化XML中繁琐的配置。

原始注解:只能自定义的Bean配置到Spring容器内

新注解:对原始注解的补充,可以将非自定义的Bean配置到Spring容器内

1. 原始注解

案例:对XML中的配置通过注解的方式代替

xml配置方式:



	
    
        
    
    	
        
    

注解配置方式:

  1. 配置注解扫描:扫描指定路径下的注解(需要引入context命名空间)

    
    
    
        
        
    
    
    
  2. 对Bean进行注解

    //@Component("userDao")	可以实例化所有的Bean	
    @Repository("userDao_id")	//标识dao层的bean实例化
    public class UserDaoImpl implements UserDao {
        @Override
        public void save() {
            System.out.println("save running...");
        }
    }
    
    @Scope("singleton")			//同配置文件中Scope标签一样,表示单例
    @Service("userService_id")		//标识service层的bean实例化
    public class UserServiceImpl implements UserService {
    
        @Value("${jdbc.driver}")	//通过Spring_SPL,用Spring容器中的键值对普通类型属性进行注入
        private String driver;
    
    //    @Autowired				//根据类型,将Spring容器中的实例自动注入到当前属性
    //    @Qualifier("userDao")		//与Autowired结合,实现根据实例id注入到当前属性
        @Resource(name = "userDao_id")	//直接通过实例id进行注入
        private UserDao userDao;
    
        @PostConstruct		//同init-method
        public void init() {
            System.out.println("init method...");
        }
        @PreDestroy			//同destory-method
        public void destory() {
            System.out.println("destory method...");
        }
    
        @Override
        public void save() {
            System.out.println(driver);
            userDao.save();
        }
    }
    
2. 新注解

案例:对XML中的配置通过注解的方式代替

XML配置方式

jdbc.driver = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/test
jdbc.username = root
jdbc.password = 123456



    
    
    
	
	
    
    

        
        
        
        
        
    
    
    
    

注解配置方式

  • 配置jdbcConfig

    @PropertySource("classpath:jdbc.properties")	//将配置文件加载到Spring容器
    public class jdbcConfig {
        @Value("${jdbc.driver}")	//获取加载到容器的配置文件键值
        private String driver;
        @Value("${jdbc.url}")
        private String url;
        @Value("${jdbc.username}")
        private String username;
        @Value("${jdbc.password}")
        private String passwd;
    
        //将返回值加载到Spring容器
        @Bean("dataSource")	
        public DataSource getDataSource() throws Exception {
            //创建连接池对象
            ComboPooledDataSource dataSource = new ComboPooledDataSource();
            //设置配置
            dataSource.setDriverClass(driver);
            dataSource.setJdbcUrl(url);
            dataSource.setUser(username);
            dataSource.setPassword(passwd);
            return dataSource;
        }
    }
    
  • SpringConfig

    @Configuration	//代表该类为核心配置类
    @ComponentScan("com.demo")	//注解扫描
    @import(jdbcConfig.class)	//导入其它配置类
    public class SpringConfig {
    
    }
    
  • 测试

    public class SpringTest {
            @Test
        public void test05() {
            ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
            UserService userService = (UserService) context.getBean("userService");	//获取容器中的实例
            userService.save();
            ((AnnotationConfigApplicationContext) context).close();
        }
    }
    
四、集成其它环境 1. 集成Juint测试

Spring继承的Junit测试可以省略创建上下文和getBean()获取容器中Bean实例的过程,通过注入的方式直接获取容器中要测试的对象

  1. 导入spring集成Junit的坐标
  2. 使用@Runwith注解替换原来的运行期
  3. 使用@ContextConfiguration指定配置文件或配置类
  4. 使用@Autowired注入需要测试的对象
  5. 创建测试方法进行测试

Junit方式

public class SpringTest {
        @Test
    public void test05() {
        //ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");	//获取上下文
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        UserService userService = (UserService) context.getBean("userService");	//获取容器中的实例
        userService.save();
        ((AnnotationConfigApplicationContext) context).close();
    }
}

Spring集成Junit

@RunWith(SpringJUnit4ClassRunner.class)	//通过Spring继承的Juint替换原来Junit
//@ContextConfiguration("classpath:applicationContext.xml")	//指定配置文件/类
@ContextConfiguration(classes = SpringConfig.class)
public class SpringJunitTest {
    @Autowired		//直接注入容器中的实例
    private UserService userService;

    @Test
    public void test01() {
        userService.save();	//使用实例
    }
}
2. 集成Web环境 2.1 原理实现

Servlet

public class UserServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
       ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
       UserService userService = app.getBean(UserService.class);
       userService.save();
    }
}

问题
当Web层Servlet调用Service层方法时,由于Service层的实例都在Spring容器中,所以每次获取Service对象都要通过加载Spring配置文件的方式获取容器中的Bean实例,造成Spring配置文件多次加载
解决方式
通过监听器Listener,当Tomcat服务器启动时就加载Spring配置文件获取ApplicationContext,并将其设置到Web全局域ServletContext中,这样所有的Servlet使用Spring容器中实例时,直接从ServletContext中获取同一个ApplicationContext即可,无需每次都加载配置文件

具体实现

  1. 配置监听器,监听服务器的启动;

    
        com.demo.listener.ContextLoaderListener
    
    
  2. 将Spring配置文件设置到Web全局初始化参数中,解耦,避免直接写文件名硬编码

    
    	contextConfigLocation
    	classpath:applicationContext.xml
    
    
  3. 服务器启动时,加载配置文件,并将获得的Spring上下文设置到ServletContext全局域对象

    public class ContextLoaderListener implements ServletContextListener {
    
        @Override
        public void contextInitialized(ServletContextEvent servletContextEvent) {
            //获取Web全局域对象ServletContext
            ServletContext servletContext = servletContextEvent.getServletContext();
            //创建Spring应用上下文
            String contextConfigLocation = servletContext.getInitParameter("contextConfigLocation");
            ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext(contextConfigLocation);
            //将上下文存储到ServletContext中
            servletContext.setAttribute("app", app);
        }
    
        @Override
        public void contextDestroyed(ServletContextEvent servletContextEvent) {
        }
    }
    
  4. 创建工具类,用来通过传入ServletContext直接获取Spring上下文对象,避免通过直接硬编码输入ServletContext属性名来获取

    public class WebApplicationContextUtils {
        public static ApplicationContext getWebApplicationContext(ServletContext servletContext) {
            return (ApplicationContext) servletContext.getAttribute("app");
        }
    }
    
2.2 Spring集成

Spring将上述的实现方式进行了封装集成,只需配置和调用其提供的接口就可以实现相同的效果

  1. 导入Maven依赖

    
    	org.springframework
    	spring-web
    	5.0.5.RELEASE
    
    
  2. 配置ContextLoaderListener监听器

    
    	contextConfigLocation
    	classpath:applicationContext.xml
    
    
    
    	
    		org.springframework.web.context.ContextLoaderListener
    	
    
    
  3. 使用WebApplicationContextUtils获得应用上下文

    public class UserServlet extends HttpServlet {
    	@Override
    	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Exception {
    		ServletContext servletContext = this.getServletContext();
    		ApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext);
    		UserService userService = app.getBean(UserService.class);
    	}
    }
    
3. JdbcTamplate

JdbcTemplate:spring框架中提供的一个对象,是对原始繁琐的Jdbc API对象的简单封装。

spring框架为我们提供了很多的 *** 作模板类。例如: *** 作关系型数据的JdbcTemplate和HibernateTemplate, *** 作nosql数据库的RedisTemplate, *** 作消息队列的JmsTemplate等等。

使用步骤

  1. 导入spring-jdbc和spring-tx坐标

    
        
            org.springframework
            spring-test
            5.0.5.RELEASE
        
        
            org.springframework
            spring-context
            5.0.5.RELEASE
        
        
            org.springframework
            spring-jdbc
            5.0.5.RELEASE
        
        
            mysql
            mysql-connector-java
            5.1.32
        
        
            c3p0
            c3p0
            0.9.1.2
            compile
        
    
    
  2. 创建数据库表和实体

    public class Account {
        private String name;
        private int age;
    
        public void setName(String name) {
            this.name = name;
        }
        public void setAge(int age) {
            this.age = age;
        }
    }
    
  3. 创建JdbcTemplate对象

    jdbc.driver = com.mysql.jdbc.Driver
    jdbc.url = jdbc:mysql://localhost:3306/test
    jdbc.username = root
    jdbc.password = 123456
    
    
    
    	
        
        
    
        
        
            
            
            
            
        
    
        
        
            
        
    
    
  4. 执行数据库 *** 作

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext.xml")
    public class SpringJdbcTest {
        @Autowired
        private JdbcTemplate jdbcTemplate;
    	//增加记录
        @Test
        public void insertTest() {
            jdbcTemplate.update("insert into ACCOUNT values(?, ?)", "张三", "10");
        }
        //删除记录
        @Test
        public void deleteTest() {
            jdbcTemplate.update("delete from ACCOUNT where name= ?", "张三");
        }
        //修改记录
        @Test
        public void updateTest() {
            jdbcTemplate.update("update ACCOUNT set name = ? where age = ?", "李四", "10");
        }
    	//查询一条记录
        @Test
        public void qurryForOneTest() {
            Account account = jdbcTemplate.queryForObject("select * from ACCOUNT where age = ?", new BeanPropertyRowMapper(Account.class), 10);
            System.out.println(account);
        }
        //查询记录指定的字段
        @Test
        public void qurryForCountTest() {
            List query = jdbcTemplate.query("select * from ACCOUNT", new BeanPropertyRowMapper(Account.class));
            System.out.println(query);
        }
        //查询多条记录
        @Test
        public void qurryForAllTest() {
            Long aLong = jdbcTemplate.queryForObject("select count(*) from Account", Long.class);
            System.out.println(aLong);
        }
    }
    

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5676431.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-16
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存