IT培训-高端面授IT培训机构
云和教育:云和数据集团高端IT职业教育品牌
  • 国家级
    全民数字素养与技能培训基地
  • 河南省
    第一批产教融合型企业建设培育单位
  • 郑州市
    数字技能人才(码农)培养评价联盟

郑州java培训教程:自定义spring

  • 发布时间:
    2016-10-12
  • 版权所有:
    云和教育
  • 分享:

郑州java培训教程:自定义spring

1 Java培训实战教程之自定义spring

1.1 描述

在企业级开发中,spring框架应用非常广。为了让已经学习过spring框架同学,可以更深入的理解和应用spring,本文将通过自定义spring,更佳系统的阐述spring核心:IoC、AOP。

IoC(Inversion of Control)控制反转:将对象的创建权交与spring框架,及将创建权反转给spring框架。IoC主要解决计算机程序的耦合问题。

AOP(Aspect Oriented Programming)面向切面编程:通过运行期动态代理实现程序功能的统一维护的一种技术。

1.2 分析

如果要实现自定义spring,可以将器拆分成多个功能实现。

阶段一:编写配置文件,服务器tomcat启动时,加载配置文件

阶段二:使用Jsoup解析xml,并封装到指定的JavaBean中

阶段三:编写工厂类用于创建指定bean,并完成property注入

阶段四:使用@Transactional进行事务管理

1.3 搭建环境

1.3.1 javabean

public class User {

private Integer uid;

private String username;

private String password;

1.3.2    dao

public interface UserDao {

/**

* 保存

* @param user

*/

public void save(User user);}

public class UserDaoImpl implements UserDao {

@Override

public void save(User user) {

//TODO 暂时只打印

System.out.println(user);

}

}

1.3.3   service

public interface UserService {

/**

* 注册

* @param user

*/

public void register(User user);

}

public class UserServiceImpl implements UserService {

private UserDao userDao;

public void setUserDao(UserDao userDao) {

this.userDao = userDao;

}

@Override

public void register(User user) {

this.userDao.save(user);

}

}

1.4   阶段一:编写配置文件,服务器启动加载

1.4.1   xml配置文件

l  在src下添加“applicationContext.xml”,并将dao和service配置到xml文件中。

l  使用<bean>标签配置一个实现类

class:    配置实现类的全限定名称

id: 进行唯一命名,用于提供给程序获得

l  使用<property>配置javabean属性的注入

name:   配置的service的属性名

ref:       配置其他bean对象的引用

<?xml version=”1.0″ encoding=”UTF-8″?>

<beans>

<!– dao –>

<bean id=”userDaoId” class=”cn.itcast.demo.dao.impl.UserDaoImpl”></bean>

<!– service –>

<bean id=”userServiceId” class=”cn.itcast.demo.service.impl.UserServiceImpl”>

<property name=”userDao” ref=”userDaoId”></property>

</bean>

</beans>

1.4.2   加载配置文件

l  tomcat启动时,加载配置文件方式总结:

1.编写Servlet,配置servlet,并添加<load-on-startup>,在init(ServletConfig)初始化方式中加载。

2.编写Filter,配置filter,在init(FilterConfig)初始化方法中加载

3.编写Listener,实现接口ServletContext,配置listener,在contextInitialized(ServletContextEvent sce)方法中加载。

l  spring采用listener方案

1.提供实现类ContextLoaderListener

2.编写全局初始化参数contextConfigLocation,用于确定xml位置

<param-value>classpath:applicationContext.xml</param-value> 加载类路径下的xml文件

<param-value>applicationContext.xml</param-value> 加载WEB-INF目录的配置文件

l  xml配置

<!– 确定xml位置 –>

<context-param>

<param-name>contextConfigLocation</param-name>

<param-value>classpath:applicationContext.xml</param-value>

</context-param><!– 配置监听器 –>

<listener>

<listener-class>cn.itcast.myspring.listener.ContextLoaderListener</listener-class>

</listener>

l  实现类

1.4.2   加载配置文件

l  tomcat启动时,加载配置文件方式总结:

1.编写Servlet,配置servlet,并添加<load-on-startup>,在init(ServletConfig)初始化方式中加载。

2.编写Filter,配置filter,在init(FilterConfig)初始化方法中加载

3.编写Listener,实现接口ServletContext,配置listener,在contextInitialized(ServletContextEvent sce)方法中加载。

l  spring采用listener方案

1.提供实现类ContextLoaderListener

2.编写全局初始化参数contextConfigLocation,用于确定xml位置

<param-value>classpath:applicationContext.xml</param-value> 加载类路径下的xml文件

<param-value>applicationContext.xml</param-value> 加载WEB-INF目录的配置文件

l  xml配置

<!– 确定xml位置 –>

<context-param>

<param-name>contextConfigLocation</param-name>

<param-value>classpath:applicationContext.xml</param-value>

</context-param><!– 配置监听器 –>

<listener>

<listener-class>cn.itcast.myspring.listener.ContextLoaderListener</listener-class>

</listener>

l  实现类

public class ContextLoaderListener implements ServletContextListener {

@Override

public void contextInitialized(ServletContextEvent sce) {

// 0 获得ServletContext对象应用

ServletContext context = sce.getServletContext();

// 1 加载配置

String config = context.getInitParameter(“contextConfigLocation”);

if(config == null){ //默认配置文件位置

config= “applicationContext.xml”;

}

InputStream xmlIs = null;

// 2  处理路径不同情况

// * classpath:applicationContext.xml –> 表示 src/applicationContext.xml

// * applicationContext.xml –> 表示 /WEB-INF/applicationContext.xml

if (config.startsWith(“classpath:”)) { // 2.1 加载 类路径 (classpath、src)下的xml

xmlIs = ContextLoaderListener.class.getClassLoader().getResourceAsStream(config.substring(“classpath:”.length()));

} else { //2.2 加载/WEB-INF/目录下的资源

xmlIs = context.getResourceAsStream(“/WEB-INF/” + config);

}

//2.3 配置文件必须存在,否则抛异常

if(xmlIs == null){

throw new RuntimeException(“资源文件[“+config+”]没有找到”);

}

//TODO 3 解析配置

if (xmlIs != null) {

System.out.println(xmlIs);

}

}

@Override

public void contextDestroyed(ServletContextEvent sce) {

}

}

1.5   阶段二:解析xml,并封装到指定javabean中

1.提供Property类,用于封装<property name=” ” ref=” “></property>

2.提供Bean类,用于封装<bean id=” ” class=” “>

一个<bean> 标签体中可以配置多个<property>需要一个容器存放,没有顺序要求,且不能重复,选择Set

3.提供BeanFactory类,并在类同提供容器存放多个Bean 类,为了方便获取使用Map。

1.5   阶段二:解析xml,并封装到指定javabean中

1.提供Property类,用于封装<property name=” ” ref=” “></property>

2.提供Bean类,用于封装<bean id=” ” class=” “>

一个<bean> 标签体中可以配置多个<property>需要一个容器存放,没有顺序要求,且不能重复,选择Set

3.提供BeanFactory类,并在类同提供容器存放多个Bean 类,为了方便获取使用Map。

1.5.1    property javabean

/**

* 用于封装 <property name=”userDao” ref=”userDaoId”></property>

*/

public class Property {//属性名称

private String name;

//另一个bean引用名

private String ref;

public Property(String name, String ref) {

super();

this.name = name;

this.ref = ref;

}

1.5.2    bean  javabean

public class Bean {

//bean名称

private String beanId;

//bean的实现类

private String beanClass;

//取值:singleton 单例,prototype 原型(多例)【扩展】

private String beanType;

//所有的property

private Set<Property> propSet = new HashSet<Property>();

public Bean(String beanId, String beanClass) {

super();

this.beanId = beanId;

this.beanClass = beanClass;

}

1.5.3    BeanFactory 工厂模式类

public class BeanFactory {

//////////////////工厂模式////////////////////////

private static BeanFactory factory = new BeanFactory();

private BeanFactory(){

}

/**

* 获得工厂实例

* @author lt

* @return

*/

public static BeanFactory getInstance() {

return factory;

}

1.5.4    BeanFactory 提供Map 缓存

//////////////////缓存所有的Bean/////////////////////////////////

//bean数据缓存集合 ,key:bean名称 ,value:bean封装对象

private static Map<String, Bean> beanData;// = new HashMap<String, String>();

static{

// 从配置文件中获得相应的数据 1.properties 2.xml

//beanData.put(“userDao”, “com.itheima.ebs.service.impl.BusinessServiceImpl”);

}public static void setBeanData(Map<String, Bean> beanData) {

BeanFactory.beanData = beanData;

}

1.5.5    修改Listener解析xml

l  使用Jsoup解析,导入jar包

l  修改contextInitialized 方法

//TODO 3 解析配置

if (xmlIs != null) {

//3.1解析

Map<String, Bean> data = parserBeanXml(xmlIs);

//3.2 将解析结果放置到工厂中

BeanFactory.setBeanData(data);

}

l  解析方法parserBeanXml(InputStream)

/**

* 将数据解析成bean

* @param xmlIs

* @return

*/

public static Map<String, Bean> parserBeanXml(InputStream xmlIs) {

try {

//0提供缓冲区域

Map<String, Bean> data = new HashMap<String, Bean>();//1解析文件,并获得Document

Document document = Jsoup.parse(xmlIs, “UTF-8”, “”);

//2 获得所有的bean元素

Elements allBeanElement = document.getElementsByTag(“bean”);

//3遍历

for (Element beanElement : allBeanElement) {

//5 将解析的结果封装到bean中

// 5.1 bean名称

String beanId = beanElement.attr(“id”);

// 5.2 bean实现类

String beanClass = beanElement.attr(“class”);

// 5.3 封装到Bean对象

Bean bean = new Bean(beanId,beanClass);

// 6 获得所有的子元素 property

Elements allPropertyElement = beanElement.children();

for (Element propertyElement : allPropertyElement) {

String propName = propertyElement.attr(“name”);

String propRef = propertyElement.attr(“ref”);

Property property = new Property(propName, propRef);

// 6.1 将属性追加到bean中

bean.getPropSet().add(property);

}

data.put(beanId, bean);

}

return data;

} catch (Exception e) {

throw new RuntimeException(e);

}

}

1.6   阶段三:完善BeanFactory获得实例

l  使用 BeanUtils.setProperty 设置数据,需要导入jar包

l  BeanFactory 提供 getBean方法

////////////////获得Bean实例///////////////////////////////////

public Object getBean(String beanId) {try {

// 通过bean 的名称获得具体实现类

Bean bean = beanData.get(beanId);

if(bean ==null) return null;

String beanClass = bean.getBeanClass();

Class clazz = Class.forName(beanClass);

Object beanObj = clazz.newInstance();

//DI 依赖注入,将在配置文件中设置的内容,通过bean的set方法设置到bean实例中

Set<Property> props = bean.getPropSet();

for (Property property : props) {

String propName = property.getName();

String propRef = property.getRef(); //另一个javabean,需要重容器中获得

Object propRefObj = getBean(propRef);

//PropertyDescriptor pd = new PropertyDescriptor(propName, clazz);

//pd.getWriteMethod().invoke(bean, propRefObj);

BeanUtils.setProperty(beanObj, propName, propRefObj);

}

return beanObj;

} catch (Exception e) {

throw new RuntimeException(e);

}

}

l  测试

//测试

UserService userService = (UserService) BeanFactory.getInstance().getBean(“userServiceId”);

userService.register(null);

1.7   阶段四:spring事务管理

1.7.1   修改自定义spring

l  提供JdbcUtils工具类,用于在当前线程中共享Connection

l  提供@Transaction 用于标记那些类需要进行事务管理

1.7.1.1         JdbcUtils工具类

l  使用ThreadLocal 保存Connection,在当前线程中共享Connection

l  并提供提交和回滚方法,自动关闭连接

public class JdbcUtils {

private static ThreadLocal<Connection> local = new ThreadLocal<Connection>();

static{

try {

//注册驱动

Class.forName(“com.mysql.jdbc.Driver”);

} catch (Exception e) {

throw new RuntimeException(e);

}

}

/**

* 获得连接

* @return

*/

public static Connection getConnection(){

try {

Connection conn = local.get();

if (conn == null) {

conn = DriverManager.getConnection(“jdbc:mysql://localhost:3306/test2”, “root”, “1234”);

local.set(conn);

}

return conn;

} catch (Exception e) {

throw new RuntimeException(e);

}

}

/**

* 提交事务

*/

public static void commit() {

Connection conn = getConnection();

DbUtils.commitAndCloseQuietly(conn);

}

/**

* 回滚事务

*/

public static void rollback() {

Connection conn = getConnection();

DbUtils.rollbackAndCloseQuietly(conn);

}

}

1.7.1.2         @Transactional

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

public @interface Transactional {}

1.7.1.3         修改BeanFactory

l  通过getBean 获得对象实例时,如果对象有@Transactional注解,将返回代理对象,对目标对象上所有的方法都进行事务管理。

l  如果没有异常提交事务,并关闭连接

l  如果有异常回滚事务,并关闭连接

public Object getBean(String beanId) {

try {

// 通过bean 的名称获得具体实现类

Bean bean = beanData.get(beanId);

if(bean ==null) return null;

String beanClass = bean.getBeanClass();

Class<?> clazz = Class.forName(beanClass);

Object beanObj = clazz.newInstance();

//DI 依赖注入,将在配置文件中设置的内容,通过bean的set方法设置到bean实例中

Set<Property> props = bean.getPropSet();

for (Property property : props) {

String propName = property.getName();

String propRef = property.getRef(); //另一个javabean,需要重容器中获得

Object propRefObj = getBean(propRef);

//PropertyDescriptor pd = new PropertyDescriptor(propName, clazz);

//pd.getWriteMethod().invoke(bean, propRefObj);

BeanUtils.setProperty(beanObj, propName, propRefObj);

}

//如果类上有注解返回代理对象

if(clazz.isAnnotationPresent(Transactional.class)){

final Object _beanObj = beanObj;

return Proxy.newProxyInstance(

clazz.getClassLoader(),

clazz.getInterfaces(),

new InvocationHandler() {

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

try {

//开启事务

JdbcUtils.getConnection().setAutoCommit(false);

//执行目标方法

Object obj = method.invoke(_beanObj, args);

//提交事务

JdbcUtils.commit();

return obj;

} catch (Exception e) {

//回顾事务

JdbcUtils.rollback();

throw new RuntimeException(e);

}

}

});

}

return beanObj;

} catch (Exception e) {

throw new RuntimeException(e);

}

}

1.7.2   初始化数据库

create table account(

id int primary key auto_increment,

username varchar(50),

money int

);

insert into account(username,money) values(‘jack’,’10000′);

insert into account(username,money) values(‘rose’,’10000′);

1.7.3   dao层

l  必须使用自定义spring提供的JdbcUtils获得连接,从而保证当前线程使用的是同一个线程。

l  通过xml配置文件创建QueryRunner实例,并注入给dao

l  扩展:如果将QueryRunner和JdbcUtils都省略,需要自己实现JdbcTemplate完成。

public class AccountDaoImpl implements AccountDao {

private QueryRunner runner;

public void setRunner(QueryRunner runner) {

this.runner = runner;

}

@Override

public void out(String outer, Integer money) {

try {

Connection conn = JdbcUtils.getConnection();

runner.update(conn, “update account set money = money – ? where username = ?”, money, outer);

} catch (Exception e) {

throw new RuntimeException(e);

}

}

@Override

public void in(String inner, Integer money) {

try {

Connection conn = JdbcUtils.getConnection();

runner.update(conn, “update account set money = money + ? where username = ?”, money,inner);

} catch (Exception e) {

throw new RuntimeException(e);

}

}

}

1.7.4   service层

l  在实现类上添加注解

@Transactional

public class AccountServiceImpl implements AccountService {private AccountDao accountDao;

public void setAccountDao(AccountDao accountDao) {

this.accountDao = accountDao;

}

@Override

public void transfer(String outer, String inner, Integer money) {

accountDao.out(outer, money);

//断电

//      int i = 1/0;

accountDao.in(inner, money);

}

}

1.7.5   编写spring配置

<!– 创建queryRunner –>

<bean id=”runner” class=”org.apache.commons.dbutils.QueryRunner”></bean><bean id=”accountDao” class=”cn.itcast.dao.impl.AccountDaoImpl”>

<property name=”runner” ref=”runner”></property>

</bean>

<!– service –>

<bean id=”accountService” class=”cn.itcast.service.impl.AccountServiceImpl”>

<property name=”accountDao” ref=”accountDao”></property>

</bean>

1.7.6   编写servlet测试

l  通过请求servlet,进行转账,此处使用固定值进行测试

public class AccountServlet extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

AccountService accountService = (AccountService) BeanFactory.getInstance().getBean(“accountService”);

accountService.transfer(“jack”, “rose”, 100);

}