SSM框架的学习笔记(更新中...)

229 阅读7分钟

SSM框架学习笔记

1、传统javaweb项目开发

在不使用框架的传统javaweb开发中,我们常见的四个层级结构是

  • domain层:用于存放javabean
  • dao层:操作数据库的简单步骤,注意这里不涉及业务逻辑,仅仅是操作数据库的单个操作。例如,
public class UserDaoImpl{
    public void addUser(User user){ // 增
    	// sql语句的增操作
    }
    public void deleteUser(User user){  // 删
    	// sql语句的删操作
    }
    public void updateUser(User user){  // 改
    	// sql语句的改操作
    }
	public User findUserByUsername(String username){  //查单个
    	// sql语句的查询操作
    }
    public List<User> findAll(){  // 查多个
    	// sql语句的查询操作
    }
}
  • service层:通过调用dao层的方法来组装成一个业务逻辑。

    例如,一个简单的用户注册业务逻辑是:

    1. 用户名是否已经存在?
    2. 若存在,则抛出一个错误给servlet处理。
    3. 若不存在,则进行把用户添加到数据库操作。
// 简单的用户注册业务
public class UserService{
	private UserDao userDao = new UserDaoImpl();
	public void regist(User form) throw UserException{
    	String username = form.getUsername();
        User user = userDao.findUserByUsername(username);
        // 业务逻辑:若user不为null,说明数据库中该用户名已存在,则注册失败,抛出异常给servlet处理
        if (user != null){
        	throw new UserException("用户名已存在!");
        }
        // 来到这里说明可以进行注册
        userDao.addUser(form);
    }
}
  • web层servlet:调用service对象执行相应的页面需求业务
public class UserServlet extends BaseServlet {
    private UserService userService = new UserService();
    public String regist(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
    	User form = new User();
        // apache的工具类BeanUtils,把一个map对象封装成javabean,前提是表单字段名和javabean属性名相同
        BeanUtils.populate(form,request.getParameterMap());
        try {
        	userService.regist(form);
        } catch (UserException e) {
        	request.setAttribute("msg",e.getMessage());
            // 打回注册页面提示错误
        }
        request.setAttribute("msg","注册成功!");
        // 跳转到注册成功的信息页面
    }
}

1.1 传统javaweb项目开发存在的一些缺点

  • Dao层频繁地创建和释放连接会造成系统资源浪费,影响性能。(可通过c3p0等连接池解决问题)
  • sql语句写死在java代码的Dao层(硬编码),修改sql语句需要改变java代码。(mybatis框架:sql语句写在配置文件,维护方便)
  • 对Dao层和Service层的对象增强,需要去修改源码,不方便。(可通过jdk动态代理/cglib动态代理解决,使用spring框架管理更方便)
  • (springMVC层优点,待更新)

2、Spring

2.1 IOC:控制反转

在传统的java代码中,创建一个类对象需要通过new语句进行,(当然还可以通过反射)。

控制反转是指,对象的创建不再是程序员主动new出来,而是spring通过解析读取配置文件自动创建类对象并存放到自身容器中。程序员需要时,从spring容器中获取相应对象。(对象的创建不再是程序员来控制而是spring控制)

2.1.1 IOC-xml配置文件方式

<!-- spring配置文件 src下文件名:applicationContext.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<beans > <!-- 这里出于篇幅考虑省略spring约束配置,约束配置最直观的体现就是idea中代码提示的可选项 -->
  <bean id="order" class="cn.myh.domain.Order" scope="singleton" >
  	<property name="oid" value="order_001" />
    	<property name="product" value="switch" />
  </bean>
  <bean id="user" class="cn.myh.domain.User" scope="singleton">
    <property name="username" value="马里奥" />
    <property name="password" value="123456" />
    <property name="order" ref="order" />
  </bean>
</beans>
public class testSpring{
    @Test
    public void testIOC(){
        // 读取spring配置文件applicationContext.xml
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        // spring考虑通用性,getBean方法返回类的最高形态Object,需要进行强转
        User user = (User)ac.getBean("user");
        System.out.println(user);
        // 可以看到输出结果不仅成功获得了User对象,还成功为属性username和password赋值为"马里奥"和"123456"
        // User里的订单对象也被赋值为实例对象
    }
}
  • spring配置文件详解:
  • 每一个bean标签代表一个类对象
    • id为该javebean的唯一身份码(身份证)
    • class为它的全限定类名
    • scope表示javabean的作用范围,可选为:
      • singleton:默认值,单例设计模式
      • prototype:多例设计模式
      • request:在WEB项目中,spring创建一个Bean的对象,并将该对象存入到request域
      • session:WEB项目,spring创建一个Bean并存在session域
  • property标签:依赖注入DI,表示为该javabean的属性值,注入属性。需要强调的是,必须提供相应属性的set方法
    • 基本类型用value属性:name为属性字段名,value为相应属性值
    • 对象类型用ref:name为属性字段名,ref为相应javabean的id值

除了为基本类型和对象类型注入属性外,spring还提供了复杂属性(array/list/map)的依赖注入方法,如下。

<bean id="user" class="cn.itcast.ioc.User">
  <!-- 为Array类型的属性进行属性注入 -->
  <!-- 对应数组属性名arrs;数组只有一个值时可用value="xxx"即可 -->
  <property name="arrs" > 
    <array>
        <value>马里奥</value>
        <value>笨币小蓝</value>
        <value>闸总陆毅</value>
    </array>
  </property>
  <!-- 为List属性进行属性注入 -->
  <property name="list" > 
    <list>
        <value>马里奥</value>
        <value>笨币小蓝</value>
        <value>闸总陆毅</value>
    </list>
  </property>
  <!-- 为List属性进行属性注入 -->
  <property name="map" >  
	<map>   
      <entry key="mario" value="马里奥" />
      <entry key="luigi" value="路易吉" />
      <entry key="qinuobiao" value="奇诺比奥" />
	</map>
</property>
</bean>

2.1.2 IOC-注解方式

从spring2.0版本后,spring引入了基于注解的配置方式。新引入的方式自然需要引入新的约束:context约束。引入约束之后,开启上下文扫描功能,spring将会扫描base-package包下,在类/方法/属性上的注解进行依赖注入。

<?xml version="1.0" encoding="UTF-8" ?>
<beans > <!-- 出于篇幅,约束略 -->
    <!-- 开启后spring会扫描cn.itcast包下的类、方法、属性上的注解 -->
    <context:component-scan base-package="cn.itcast"></context:component-scan>
</beans>
@Service(value="userService")
public class UserService{
  @Autowired  //在属性上使用Autowired注解,将自动根据属性类型完成依赖注入 
  private UserDao userDao;
  @Resource(name="user")  // 除了上面这种方法注入属性,还可以用这种方法,名字和组件的value必须对应
  private User user;
}
@Component(value="user")
public class User{
	//...
}
  • 作用在类上的注解:目前功能一样,是spring为了让标注类本身的用途更清晰明确,而且会在后续进行增强。
    • @Component 作用在类上,等价于 bean id="user" class="" 。其中value值相当于id值
    • @Controller:用在web层
    • @Service:用在service层
    • @Repository:持久层
  • 作用在属性上的注解:
    • @Autowired:自动根据属性类型去找相应类型的对象进行注入
    • @Resource:需要显式声明value值,根据value去寻找相应对象完成注入

2.2 AOP:面向切面编程思想

面向切面编程思想,是指对原有代码进行功能拓展时,通过不修改源码的方式来实现增强的一种思想。

  • AOP操作相关术语
    • Joinpoint(连接点):任何可能被增强的方法,都可以称之为连接点。
    • Pointcut(切入点):被增强了的方法,称之为切入点。
    • Advice (通知/增强):要添加的增强操作,例如需要在某一方法执行前后,打印时间。打印时间这一操作就是增强。
      • Advice可分为:前置通知,后置通知,环绕通知,异常抛出通知,最终通知,引介通知。(这里通知/增强的说法没有固定)
      • 前置通知、后置通知、环绕通知:如字面意思,分别在方法的前、后、以及前和后执行的通知。
      • 异常抛出通知:当被增强的方法抛出异常时,才会执行该通知。类似于try..catch的catch部分。
      • 最终通知:不管是否抛出异常都会执行的通知。类似于try..catch..finally的finally部分。
    • Aspect (切面): 切入点和通知的结合体。即,把增强的内容应用到具体方法上的全过程。
  • 通过AspectJ来实现AOP操作
    • 相关jar包和aop约束:aopalllliance.jar,aspectj.weaver.jar,spring-aop.jar,spring-aspects.jar
    • 定义切入点语法:两个execution语句可以用||来连接,表示任意匹配一个都可以
    execution( <访问修饰符>?<返回类型><方法名> ( <参数> ) <异常> )
    // 表示对Book类下的add方法增强,..表示任意个数的参数
    1. execution(* cn.itcast.aop.Book.add(..));
    // 对Book类下所有方法增强
    2. execution(* cn.itcast.aop.Book.*(..));
    // 对所有类下的所有方法增强 
    3. execution(* *.*(..));
    // 匹配所有以save开头的方法
    4. execution(* save*(..));
    
  1. 基于aspectj的xml配置实现
<beans >
    <bean id="book" class="cn.myh.aop.Book"></bean>
    <bean id="myBook" class="cn.myh.aop.MyBook"></bean>
    <!-- 配置aop -->
    <aop:config>        
        <!-- 配置切面,Book是被增强的,而MyBook是增强的内容 -->
        <aop:aspect ref="myBook">
            <!-- 配置切入点,本例切入点为Book的add()方法,即要增强该方法 -->
            <aop:pointcut id="pointcut1" 
                      expression="execution(* cn.myh.aop.Book.add(..))"/>
            <!-- aop:before表示前置增强,使用myBook的beforeAdd()方法,对切入点进行增强 -->
            <aop:before method="beforeAdd" pointcut-ref="pointcut1"></aop:before>
            <aop:after-returning method="AfterAdd" pointcut-ref="pointcut1"
                                 returning="ret"></aop:before>
            <aop:arround method="arroundAdd" pointcut-ref="pointcut1" />
            <aop:after-throwing method="afterThrowing" 
                                pointcut-ref="pointcut1" throwing="e" />
            <aop:after method="finally" pointcut-ref="pointcut1" />
        </aop:aspect>
    </aop:config>
</beans>
@Component
@Aspect
public class MyBook{
    // 增强方法可以带参数(可选),参数必须是org.aspectJ.JoinPoint类型,而不是Joinpoint!
    // org.aspectJ.JoinPoint用于描述目标方法,可以获得目标方法签名(修饰符,返回值,参数)
    public void beforeAdd(JoinPoint joinpoint){
        joinpoint.getSignature(); // 方法签名
		joinpoint.getSignature().getName() // 方法名
    }
    // 后置通知,第一个参数可选,可以获得目标方法的返回值,即第二个参数。注意,类型为Object,名称ret必须与配置文件的相同
    public void AfterAdd(JoinPoint joinpoint,Object ret){
        joinpoint.getSignature(); // 方法签名
        joinpoint.getSignature().getName() // 方法名
    }
    // 环绕通知 - 返回值必须是Object,需要手动执行目标方法,返回目标方法的结果
    // 参数必须是ProceedingJoinPoint类型
    public Object aroundAdd(ProceedingJoinPoint joinpoint) throws Throwable{
        System.out.println("前置")
        // 手动执行目标方法
        Object obj = joinpoint.proceed();
        System.out.println("后置")
        return obj;
    }
    // 异常通知 异常名称e和xml配置文件必须要对应
    public void afterThrowing(JoinPoint joinpoint , Throwable e){
        System.out.println("异常抛出:"+e.getMessage());
    }
    // 最终通知 
    public void finally(JoinPoint joinpoint){
        
    }
}
  1. 基于aspectj的注解方式
    • 配置文件中开启aop的自动代理扫描
<beans >
    <!-- 开启bean的注解扫描 -->
    <context:component-scan base-package="cn.itcast"></context:component-scan>
    <!-- 开启AOP的自动代理注解 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
// 在实现增强的类上使用注解
@Component("myBook")
@Aspect   // 切面注解声明
public class MyBook {
    // 前置增强,这里即是切入点,无需显式配置,但切入点仅该方法可见
    @Before(value="execution(* cn.itcast.aop.Book.*(..))")
    public void beforeAdd(){
        System.out.println("前置增强.....");
    }
    
    // 也可以配置公共的切入点表达式
    @Pointcut("execution(* cn.itcast.aop.Book.*(..))")
    private void myPointcut(){}
    
    // 后置增强,value值不再写切入点表达式,而是使用公共表达式
    @AfterReturning(value="myPointcut()",returning="ret")
    public void afterReturning(JoinPoint joinpoint,Object ret){
        System.out.println("后置增强.....");
    }
}
  • 常见的AspectJ注解类型
    • @Before 前置通知
    • @AfterReturning 后置通知
    • @Around 环绕通知
    • @AfterThrowing 异常抛出通知
    • @After 最终通知
    • @Pointcut 用于声明切入点

3、Mybatis

4、SpringMVC