Spring
发布日期:2021-08-20 07:55:12 浏览次数:7 分类:技术文章

本文共 15106 字,大约阅读时间需要 50 分钟。

hot3.png

 

基本概念

90%web项目都会用到Spring

Spring除了可以和Hibernate整合也可以和JDBC整合;

Spring的两个最基本的东西是:IOC(控制反转,也叫“依赖注入”)、AOP(面向切面);

IOC

【基本概念】:

<bean

id="userAction"

class="org.zttc.itat.spring.action.UserAction"

scope="prototype">//prototype:多例;singlton:单例(默认)

【提示】:

ActionController)中的状态没有修改就用单例,有修改的话就用多例。比如,添加UserAction,一般都会用多例,因为每次请求添加的内容都不一样。

【使用属性注入】:

<bean id="userService" class="org.zttc.itat.spring.service.UserService">

<!-- name中的值会在userService对象中调用setXX方法来注入,诸如:name="userDao"在具体注入时会调用setUserDao(IUserDao userDao)来完成注入ref="userDao"表示是配置文件中的bean中所创建的DAOid -->

    <property name="userDao" ref="userDao"></property>

</bean>

【使用构造函数注入(不常用)】:

public class UserAction {

    private User user;

    private IUserService userService;

    private int id;

    private List<String> names;

    // 构造函数

    public UserAction(IUserService userService) {

       super();

       this.userService = userService;

    }

在配置文件中使用构造函数注入:

<!-- 以下是使用构造函数来注入,不常用,基本都是使用set方法注入 -->

<bean id="userAction"

class="org.zttc.itat.spring.action.UserAction"

scope="prototype">

    <constructor-arg ref="userService"/>// 构造函数的参数,依顺序往下写

</bean>

【自动注入(不常用,也不建议使用)】:

// autowire会自动找到属性和对应的set方法进行注入(若值为default则不自动注入)

<bean id="userAction"

class="org.zttc.itat.spring.action.UserAction"

scope="prototype" autowire="byname ">

    <!--<property ref="userService"/>-->

</bean>

【提示】:

Spring3.0后提供了基于Annotation注入。

使用Annotation

【基本使用方法】:

beans.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:aop="http://www.springframework.org/schema/aop"

     xmlns:context="http://www.springframework.org/schema/context"

     xsi:schemaLocation="http://www.springframework.org/schema/beans

……

>

   <!-- 打开SpringAnnotation支持 -->

   <context:annotation-config/>

   <!-- 设定Spring 去哪些包中找Annotation -->

   <context:component-scan base-package="org.zmj.test.spring"/>

   <!-- 打开基于AnnotationAOP -->

   <aop:aspectj-autoproxy/>

</beans>

将相关类注入

注入UserDao

//等于完成了<bean id="userDao" class="org.zttc.itat.spring.UserDao"/>

//@Component("userDao")//公共的创建beanAnnotation,所有的类都能用

@Repository("userDao")//@Repository一般用于DAO的注入

public class UserDao implements IUserDao {

    @Override

    public void add(User user) {

       System.out.println("添加了"+user);

    }

    ……

}

注入UserService

//@Component("userService")

@Service("userService")//业务层一般使用@Service

public class UserService implements IUserService {

    private IUserDao userDao;

    private IMessageDao messageDao;

   

    @Resource(name="messageDao")

    public void setMessageDao(IMessageDao messageDao) {

       this.messageDao = messageDao;

    }

    //默认通过名称注入,在JSR330中提供了@Inject来注入

    //@Autowired也可以,就是默认注入使用类型注入,不建议使用

    @Resource(name="userDao")

    public void setUserDao(IUserDao userDao) {

       this.userDao = userDao;

    }

}

注入UserAction

//@Component("userAction")

@Controller("userAction")//MVC的控制层一般使用@Controller

@Scope("prototype")

public class UserAction {

    private User user;

    private IUserService userService;

    private int id;

    private List<String> names;

   

}

【提示】:

项目比较大的时候一般不适用Annotation。因为在大项目中的代码结构是按模块来划分的,类非常多,使用Annotation的话类的依赖关系不明显,但是在配置文件中看的话就会一目了然。

AOP(重点)

【实例1】:AOP-模拟(静态代理)

背景:假设我们的项目已经上线,已经在运行,突然客户提出要求,要在项目中加上日志的输出。

我们直观的想法是,创建一个类用来输出日志,然后在原来的代码里加入输出日志的代码。但是这个破坏了原来的代码。解决方法是使用代理。

比如,在我们的代码里,我们创建一个UserProxyDao类,这个类实现了IUserDao,然后实现接口中的方法,然后在这些方法前加入输出日志的代码,这样就不会破坏原来的代码了。

代码内容:

@Component("userProxyDao")

public class UserProxyDao implements IUserDao {

    private IUserDao userDao;

    @Resource

    public void setUserDao(IUserDao userDao) {

       this.userDao = userDao;

    }

    @Override

    public void add(User user) {

       Logger.info("添加了用户");

       userDao.add(user);

    }

    @Override

    public void delete(int id) {

       Logger.info("删除了用户");

       userDao.delete(id);

    }

    @Override

    public User load(int id) {

       return userDao.load(id);

    }

}

UserSerivce中用到了这些方法,所以要在UserService中注入userProxyDao

【提示】:

以上是“静态代理”的实现。

这样虽然解决了问题且没有破坏原来的代码,但是这样还有一个问题,就是,如果还想给其他的Dao(如MessageDao)添加日志,我们还要再创建一个MessageProxyDao用来输出日志,如果哪一天不想输出日志了,还要再创建新的代理,这样肯定很麻烦,不可行。

经过分析,我们发现,输出日志的代码都是独立的,和业务逻辑无关,所以,我们可以讲这些代码抽出来,然后注入到相关的代码中。

【实例2】:AOP-手动实现

动态代理是指通过一个代理对象来创建需要的业务对象,然后在这个代理对象中统一进行各种需求的处理。

【步骤】:

步骤1写一个类实现InvocationHandler接口

步骤2创建一个代理对象;

步骤3创建一个方法来创建对象,方法的参数是要代理的对象

创建代理对象:

//1、写一个类实现InvocationHandler接口*/

public class LogProxy implements InvocationHandler {

    private LogProxy(){}

    //2、创建一个代理对象

    private Object target;

    //3、创建一个方法来生成对象,这个方法的参数是要代理的对象,

//   getInstacne所返回的对象就是代理对象

    public static Object getInstance(Object o) {

       //3.1、创建LogProxy对象

       LogProxy proxy = new LogProxy();

       //3.2、设置这个代理对象

       proxy.target = o;

       //3.3、通过Proxy的方法创建代理对象,

//      第一个参数是要代理对象的classLoader

        //      第二个参数是要代理对象实现的所有接口,

//      第三个参数是实现类InvocationHandler的对象

       //此时的result就是一个代理对象,代理的是o

       Object result = Proxy.newProxyInstance(

o.getClass().getClassLoader(),

o.getClass().getInterfaces(),

proxy);

       return result;

    }

    /**当有了代理对象之后,不管这个代理对象执行什么方法,

* 都会调用以下的invoke方法*/

    @Override

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

           throws Throwable {

       /**控制哪些方法做日志输出 - 判断方法名称*/

       //if(method.getName().equals("add")

||method.getName().equals("delete")) {

       //  Logger.info("进行了相应的操作");

       //}

      

       /**控制哪些方法做日志输出 - 使用Annotation*/

       //以下代码可以放在方法执行前、方法执行后也可放在异常中执行

       if(method.isAnnotationPresent(LogInfo.class)) {

           LogInfo li = method.getAnnotation(LogInfo.class);

           Logger.info(li.value());

       }

       // 执行方法

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

       return obj;

    }

}

将代理对象注入到Spring中:(因为没有getset所以不能使用注解注入)

   // 以下代码的意思是,使用LoginProxy类使用getInstance创建userDao

// userDao会和注解中的“userDao”进行匹配,这个userDao会传入代理中的

// getInstance参数中

<bean id="userDynamicDao"

class="org.zttc.itat.spring.proxy.LogProxy"

          factory-method="getInstance">

         <constructor-arg ref="userDao"/>

   </bean>

   <bean id="messageDynamicDao"

class="org.zttc.itat.spring.proxy.LogProxy"

factory-method="getInstance">

         <constructor-arg ref="messageDao"/>

   </bean>   

UserService中使用userDynamicDao

//@Component("userService")

@Service("userService")//业务层一般使用@Service

public class UserService implements IUserService {

    private IUserDao userDao;

    private IMessageDao messageDao;

   

    @Resource(name="messageDynamicDao")

    public void setMessageDao(IMessageDao messageDao) {

       this.messageDao = messageDao;

    }

    //默认通过名称注入,在JSR330中提供了@Inject来注入

    @Resource(name="userDynamicDao")

    public void setUserDao(IUserDao userDao) {

       this.userDao = userDao;

    }

}

使用Annotation来控制要输出的信息:

@Retention(RetentionPolicy.RUNTIME)

public @interface LogInfo {

    public String value() default"";

}

在接口上(我们这里是基于接口的代理)添加该注解:

public interface IUserDao {

    @LogInfo("添加用户信息")

    public void add(User user);

    @LogInfo("删除用户信息")

    public void delete(int id);

    public User load(int id);

}

【小结】:

所谓AOP(切面)就是将关注的模块抽出来做成一个独立的部分(切面)。

【实例3】:AOP- Annotation

步骤1设置Schema、打开基于AnnotationAOP

<?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:aop="http://www.springframework.org/schema/aop"

     xmlns:context="http://www.springframework.org/schema/context"

     xsi:schemaLocation="http://www.springframework.org/schema/beans

         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

         http://www.springframework.org/schema/context        http://www.springframework.org/schema/context/spring-context-3.0.xsd

         http://www.springframework.org/schema/aop         http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

   <!-- 打开SpringAnnotation支持 -->

   <context:annotation-config/>

   <!-- 设定Spring 去哪些包中找Annotation -->

   <context:component-scan base-package="org.zttc.itat.spring"/>

   <!-- 打开基于AnnotationAOP -->

   <aop:aspectj-autoproxy/>

</beans>

步骤2创建代理类(Spring使用的是第三方的切面技术,所以要另外导入包:

aopalliance.jar,aspectjrt.jar,aspectjweaver.jar

@Component("logAspect")//让这个切面类被Spring所管理

@Aspect//申明这个类是一个切面类(需要导入第三方包)

public class LogAspect {

   

    /**

     * execution(* org.zttc.itat.spring.dao.*.add*(..))

     * 第一个*表示任意返回值

     * 第二个*表示 org.zttc.itat.spring.dao包中的所有类

     * 第三个*表示以add开头的所有方法

     * (..)表示任意参数

     */

    @Before("execution(* org.zttc.itat.spring.dao.*.add*(..))||" +

           "execution(* org.zttc.itat.spring.dao.*.delete*(..))||" +

           "execution(* org.zttc.itat.spring.dao.*.update*(..))")

    public void logStart(JoinPoint jp) {

       //得到执行的对象

       System.out.println(jp.getTarget());

       //得到执行的方法

       System.out.println(jp.getSignature().getName());

       Logger.info("加入日志");

    }

    /**函数调用完成之后执行*/

    @After("execution(* org.zttc.itat.spring.dao.*.add*(..))||" +

           "execution(* org.zttc.itat.spring.dao.*.delete*(..))||" +

           "execution(* org.zttc.itat.spring.dao.*.update*(..))")

    public void logEnd(JoinPoint jp) {

       Logger.info("方法调用结束加入日志");

    }

   

    /** 函数调用中执行 */

    @Around("execution(* org.zttc.itat.spring.dao.*.add*(..))||" +

           "execution(* org.zttc.itat.spring.dao.*.delete*(..))||" +

           "execution(* org.zttc.itat.spring.dao.*.update*(..))")

    public void logAround(ProceedingJoinPoint pjp) throws Throwable {

       Logger.info("开始在Around中加入日志");

       pjp.proceed();//执行程序

       Logger.info("结束Around");

    }

}

【实例3】:AOP-XML

首先,第三方包要导入、Schema头要设置。

创建切面(代理类):

@Component("logAspect")//让这个切面类被Spring所管理

public class LogAspect {

    public void logStart(JoinPoint jp) {

       //得到执行的对象

       System.out.println(jp.getTarget());

       //得到执行的方法

       System.out.println(jp.getSignature().getName());

       Logger.info("加入日志");

    }

    public void logEnd(JoinPoint jp) {

       Logger.info("方法调用结束加入日志");

    }

    public void logAround(ProceedingJoinPoint pjp) throws Throwable {

       Logger.info("开始在Around中加入日志");

       pjp.proceed();//执行程序

       Logger.info("结束Around");

    }

}

配置文件中配置该切面:

   <!-- 打开SpringAnnotation支持 -->

   <context:annotation-config/>

   <!-- 设定Spring 去哪些包中找Annotation -->

   <context:component-scan base-package="org.zttc.itat.spring"/>

  

   <aop:config>

   <!-- 定义切面 -->

       <aop:aspect id="myLogAspect" ref="logAspect">

       <!-- 在哪些位置加入相应的Aspect -->

           <aop:pointcut

id="logPointCut"

expression="

execution(* org.zttc.itat.spring.dao.*.add*(..))||

execution(* org.zttc.itat.spring.dao.*.delete*(..))||

execution(* org.zttc.itat.spring.dao.*.update*(..))"

/>

           <aop:before method="logStart" pointcut-ref="logPointCut"/>

           <aop:after method="logEnd" pointcut-ref="logPointCut"/>

           <aop:around method="logAround" pointcut-ref="logPointCut"/>

       </aop:aspect>

   </aop:config>

SpringJDBC整合

步骤1导入Spring包和数据库驱动包;

步骤2选择一个数据源(DBCPC3P0);

步骤3导入数据源的包;

步骤4beans.xml中创建dataSource数据源;

步骤5创建数据库的属性文件,存放连接数据库的连接信息;

步骤6beans.xml中导入属性文件;

步骤7创建UserDao并在UserDao中创建JDBCTemplate对象(通过JDBCTemplate可以方

便操作数据库);

步骤8Dao注入DataSource并创建JDBCTemplate

步骤9完成数据的增删改查

beans.xml文件:

  <bean id="dataSource"

         class="org.apache.commons.dbcp.BasicDataSource"

destroy-method="close">

    <property name="driverClassName" value="${jdbc.driverClassName}"/>

    <property name="url" value="${jdbc.url}"/>

    <property name="username" value="${jdbc.username}"/>

    <property name="password" value="${jdbc.password}"/>

</bean>

<!-- 导入Src目录下的jdbc.properties文件 -->

<context:property-placeholder location="jdbc.properties"/>

连接数据库的属性文件内容:

jdbc.driverClassName=com.mysql.jdbc.Driver

jdbc.url = jdbc:mysql://localhost:3306/spring_teach

jdbc.username = root

jdbc.password = 123456

创建User实体:

根据实体内容创建对应的数据库表:

创建UserDaoJDBCTemplate

@Repository("userJdbcDao")

public class UserDao implements IUserDao {

   

    private JdbcTemplate jdbcTemplate;

   

    @Resource

    public void setDataSource(DataSource dataSource) {

       jdbcTemplate = new JdbcTemplate(dataSource);

    }

    @Override

    public void add(User user,int gid) {

       jdbcTemplate.update("insert into t_user(username,password,nickname,gid) value (?,?,?,?)",

              user.getUsername(),user.getPassword(),user.getNickname(),gid);

    }

}

使用Spring集成测试:

/**当使用了以下注解之后,就可以直接在Test中进行依赖注入*/

//Junit运行在Spring的测试环境中

@RunWith(SpringJUnit4ClassRunner.class)

//加载beans.xml文件

@ContextConfiguration("/beans.xml")

public class TestJdbc {

    @Resource(name="userJdbcDao")//注意:名字要和注解中配置的名字一致!

    private IUserDao userJdbcDao;

    @Resource(name="groupJdbcDao")

    private IGroupDao groupJdbcDao;

   

    @Test

    public void testAdd() {

       Group g = new Group();

       g.setName("文章审核人员");

       groupJdbcDao.add(g);

       System.out.println(g.getId());

       User u = new User("tangsheng","123","唐僧");

       userJdbcDao.add(u, 1);

    }

}

添加Group(用户属于组)

主要是想介绍如何在添加一个实体后返回主键的((非重点):

@Repository("groupJdbcDao")

public class GroupJdbcDao implements IGroupDao {

    private JdbcTemplate jdbcTemplate;

   

    @Resource

    public void setDataSource(DataSource dataSource) {

       jdbcTemplate = new JdbcTemplate(dataSource);

    }

    @Override

    public void add(final Group group) {

       /**

        * 通过以下方法可以添加一个对象,并且获取这个对象自动递增的id

        */

       KeyHolder keyHolder = new GeneratedKeyHolder();

       jdbcTemplate.update(new PreparedStatementCreator() {

           @Override

           public PreparedStatement

createPreparedStatement(Connection con)

                  throws SQLException {

              String sql = "insert into t_group (name) value(?)";

              PreparedStatement ps =

con.prepareStatement(sql,new String[]{

"id"});

              ps.setString(1, group.getName());

              return ps;

           }

       },keyHolder);

       group.setId(keyHolder.getKey().intValue());

    }

}

@Repository("userJdbcDao")

public class UserDao implements IUserDao {

   

    private JdbcTemplate jdbcTemplate;

   

    @Resource

    public void setDataSource(DataSource dataSource) {

       jdbcTemplate = new JdbcTemplate(dataSource);

    }

    @Override

    public void add(User user,int gid) {

       jdbcTemplate.update("

insert into t_user

(username,password,nickname,gid)

value (?,?,?,?)",

              user.getUsername(),

user.getPassword(),

user.getNickname(),gid);

    }

    @Override

    public void update(User user) {

       jdbcTemplate.update("update t_user set username=?,password=?,nickname=? where id=?",

              user.getUsername(),user.getPassword(),user.getNickname(),user.getId());

    }

    @Override

    public void delete(int id) {

       jdbcTemplate.update("delete from t_user where id=?",id);

    }

    @Override

    public User load(int id) {

       String sql = "select t1.id uid,t1.*,t2.* from t_user t1 left join t_group t2 on(t1.gid=t2.id) where t1.id=?";

       /*

        * 第一个参数是SQL语句

        * 第二个参数是SQL语句中的参数值,需要传入一个对象数组

        * 第三个参数是一个RowMapper,这个rowMapper可以完成一个对象和数据库字段的对应,实现这个RowMapper需要

        * 实现mapRow方法,在mapRow方法中有rs这个参数,通过rs可以有效的获取数据库的字段

        * 如果这个方法在该DAO中会被重复使用,建议通过内部类来解决,而不要使用匿名的内部类

        */

       User u = (User)jdbcTemplate.queryForObject(sql, new Object[]{

id},new UserMapper());

       return u;

    }

    @Override

    public List<User> list(String sql,Object[] args) {

       String sqlCount = "select count(*) from t_user";

       //获取整数值

       int count = jdbcTemplate.queryForInt(sqlCount);

       System.out.println(count);

       String nCount = "select nickname from t_user";

       //获取String类型的列表

       List<String> ns = jdbcTemplate.queryForList(nCount,String.class);

       for(String n:ns) {

           System.out.println("--->"+n);

       }

       String tSql = "select username,nickname from t_user";

       //无法取出user

       /*List<User> us = jdbcTemplate.queryForList(tSql, User.class);

       for(User u:us) {

           System.out.println(u);

       }*/

       //对象数组也无法返回

       /*List<Object[]> os = jdbcTemplate.queryForList(tSql, Object[].class);

       for(Object[] oo:os) {

           System.out.println(oo[0]+","+oo[1]);

       }*/

      

       List<User> us = jdbcTemplate.query(tSql,new RowMapper<User>(){

           @Override

           public User mapRow(ResultSet rs, int rowNum) throws SQLException {

              User u = new User();

              u.setNickname(rs.getString("nickname"));

              u.setUsername(rs.getString("username"));

              return u;

           }

       });

       for(User u:us) {

           System.out.println(u);

       }

       return jdbcTemplate.query(sql, args, new UserMapper());

    }

   

    private class UserMapper implements RowMapper<User> {

       @Override

       public User mapRow(ResultSet rs, int rowNum) throws SQLException {

           Group g = new Group();

           g.setName(rs.getString("name"));

           g.setId(rs.getInt("gid"));

           User u = new User();

           u.setGroup(g);

           u.setId(rs.getInt("uid"));

           u.setNickname(rs.getString("nickname"));

           u.setPassword(rs.getString("password"));

           u.setUsername(rs.getString("username"));

           return u;

       }

    }

}

转载于:https://my.oschina.net/u/1273559/blog/347549

转载地址:https://blog.csdn.net/weixin_33672109/article/details/92089057 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:[百度SEM竞价学习之三]——SEM搜索推广方案制作
下一篇:初等数学小丛书

发表评论

最新留言

能坚持,总会有不一样的收获!
[***.219.124.196]2024年04月15日 12时26分58秒