美文网首页
02-SSM框架开发

02-SSM框架开发

作者: 努力学习的lfk | 来源:发表于2023-12-21 22:39 被阅读0次

    2023-12-22


    1、Java原生开发存在问题

    1.1、DAO层存在问题


    1)每次都要做大量的相同的操作
    2)对执行sql语句过程中所出现的各种异常和资源释放进行处理
    3)需要更多的代码来提取结果并将它们映射到对象实例中

    1.2、Service层存在问题


    1)当功能越来越多, 与业务逻辑无关, 但是被多处业务逻辑模块共享的代码(比如判断用户登录, 日志管理, 权限检查, 事务管理等)修改和维护的成本大大增加
    2)没有做到资源复用,创建了许多重复对象。造成大量资源浪费*
    3)更换实现类需要改动多个地方。修改和维护的成本大大增加。

    参考CSDN博主「灵魂相契的树」的原创文章,原文链接:https://blog.csdn.net/yzhcjl_/article/details/132520053
    参考知乎作者「码农出击」的原创文章,原文链接:https://zhuanlan.zhihu.com/p/493973685

    1.3、Controller层存在问题


    1)每个servlet处理特定的请求,如果servlet过多,会导致在web.xml内容过多(web.xml文件方式配置)、servlet类文件数量过多。
    2)通常使用JSP进行页面展示,对前端不是特别友好。
    3)servlet具有容器依赖性,必须放在服务器中运行,不利于单元测试。JSP 页面无法直接访问需要有 Java Servlet 容器来编译和运行

    2、解决方案

    SSM框架开发.png
    SpringMVC
    1)
    2)
    Spring
    1)Spring IOC有许多优点,它可以帮助改善代码的结构、可维护性和可测试性。低耦合——代码分离、便于代码复用
    2)Spring AOP能帮助我们无耦合的实现日志记录,性能统计,安全控制等。高内聚——结构清晰、便于开发维护

    Mybatis
    1)避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。resultType自动映射查询结果,resultMap复杂场景下构建一个结果映射

    参考CSDN博主「向上爬的小蜗牛」的原创文章,原文链接:https://blog.csdn.net/LyySwx/article/details/90900013


    3、Spring

    3.1、IOC

    IoC(inversion of control):把对象的创建、赋值、管理工作都交给代码之外的容器实现,即对象的创建是由其他外部资源完成。

    相关概念
    1)控制:创建对象,对象的属性赋值,对象之间的关系管理。
    2)反转:把原来的开发人员管理、创建对象的权限转移给代码之外的容器实现。由容器代替开发人员管理对象、创建对象、给属性赋值。
    3)正转:由开发人员在代码中,使用new构造方法创建对象等,开发人员主动管理对象。

    /*
    示例:
    Spring IoC 容器创建好 B 的实例对象后并赋值给 A 对象中的 b 属性(成员变量)的过程,就是所谓的「依赖注入
    */
    public class A {
        private B b;
        // 省略 getter 和 setter
        // 省略构造方法
    }
    

    IoC的技术实现:
    DI (Dependency Injection,依赖注入):是IoC 的一种具体实现方式,它是指将对象所依赖的其他对象(即依赖)通过构造函数、Setter 方法或其他方式注入到对象中,从而消除了对象之间的耦合关系。
    只需要在程序中提供要使用的对象名称就可以,至于对象如何在容器中创建、赋值、查找都由容器内部实现

    3.1.1、依赖注入的方式

    1)基于构造方法的依赖注入

    构造方法的优势: 保证一些必要的属性在Bean实例化时就得到设置,并且确保了Bean实例在实例化后就可以使用。
    构造方法的弊端: 在创建对象时,如果用不到这些数据,也必须提供。

    标签:<constructor-arg></constructor-arg>
    属性:

    type:用于指定要注入的数据类型,该数据类型也是构造函数中的某些类型或者某些参数
    index:指定要注入的数据给构造函数中指定索引位置的参数赋值,参数索引的位置从0开始
    name:用于指定构造函数中指定名称的参数赋值(常用)
    (以上三个标签根据需要选一个就行)
    一一一一一一一一一一一一一一一一一
    value:用于提供基本类型和String类型的数据
    ref:用于指定其他Bean类型的数据,需填写在SpringIOC容器中的唯一id

    • Dao类
    //基于构造方法注入
    public class UserDao {
        private String name;
        private int age;
    
        public UserDao(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public void insert(){
            System.out.println("保存用户:"+age+"岁"+name);
        }
    }
    
    • Service类
    //基于构造方法注入
    public class UserService {
        private UserDao userDao;
    
        public UserService(UserDao userDao) {
            this.userDao = userDao;
        }
    
        public void add(){
            userDao.insert();
        }
    }
    
    • XML配置文件
      Ⅰ、按名称匹配入参
        <!--按名称匹配入参-->
        <bean id="userDao" class="org.example.dao.UserDao">
            <constructor-arg name="name" value="李四"></constructor-arg>
            <constructor-arg name="age" value="55"></constructor-arg>
        </bean>
        <bean id="userService" class="org.example.service.UserService">
            <constructor-arg name="userDao" ref="userDao"></constructor-arg>
        </bean>
    

    Ⅱ、按类型匹配入参

        <!--按类型匹配入参-->
        <bean id="userDao" class="org.example.dao.UserDao">
            <constructor-arg type="java.lang.String" value="李四"></constructor-arg>
            <constructor-arg type="int" value="55"></constructor-arg>
        </bean>
        <bean id="userService" class="org.example.service.UserService">
            <constructor-arg type="org.example.dao.UserDao" ref="userDao"></constructor-arg>
        </bean>
    

    Ⅲ、按索引匹配入参

        <!--按索引匹配入参-->
        <bean id="userDao" class="org.example.dao.UserDao">
            <constructor-arg index="0" value="李四"></constructor-arg>
            <constructor-arg index="1" value="55"></constructor-arg>
        </bean>
        <bean id="userService" class="org.example.service.UserService">
            <constructor-arg index="0" ref="userDao"></constructor-arg>
        </bean>
    

    Ⅳ、联合使用类型和索引匹配入参(适用于有多个构造函数,根据索引入参无法匹配的情况)

        <!--联合使用类型和索引匹配入参-->
        <bean id="userDao" class="org.example.dao.UserDao">
            <constructor-arg index="0" type="java.lang.String" value="李四"></constructor-arg>
            <constructor-arg index="1" type="int" value="55"></constructor-arg>
        </bean>
        <bean id="userService" class="org.example.service.UserService">
            <constructor-arg index="0" type="org.example.dao.UserDao" ref="userDao"></constructor-arg>
        </bean>
    
    • 测试类
    //基于构造方法注入
        @Test
        public  void  test(){
            //读取配置文件
            ApplicationContext ctx=new ClassPathXmlApplicationContext("spring-config.xml");
            //获取bean的实例
            UserService userService =(UserService) ctx.getBean("userService");
            //调用方法
            userService.add();
        }
    
    • 运行结果

    2)基于 Setter 的依赖注入

    set方法的优势: 创建对象时没有明确的限制,可以直接使用默认构造函数,具有可选择性和灵活性高的优点。
    set方法的弊端: 如果有某个成员必须有值的时候,则获取对象时有可能set方法无法保证一定注入。

    标签:<property></property>
    属性:

    name:用于指定注入时调用的set方法名
    value:用于提供基本类型和String类型的数据
    ref:用于指定其他Bean类型的数据,需填写在SpringIOC容器中的唯一id

    • Dao类
    //基于set方法注入
    public class UserDao {
        private String name;
        private int age;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public void insert(){
            System.out.println("保存用户:"+age+"岁"+name);
        }
    }
    
    • Service类
    //基于setter方法注入
    public class UserService {
        private UserDao userDao;
    
        public void add(){
            userDao.insert();
        }
    
        public UserDao getUserDao() {
            return userDao;
        }
    
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
    }
    
    • XML配置文件
    <!--基于setter方法注入-->
        <bean id="userDao" class="org.example.dao.UserDao">
            <property name="name" value="张三"></property>
            <property name="age" value="15"></property>
        </bean>
        <bean id="userService" class="org.example.service.UserService">
            <property name="userDao" ref="userDao"></property>
        </bean>
    
    • 测试类
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = "classpath:spring-config.xml")
    public class UserServiceTest {
        //基于setter方法注入
        @Test
        public  void  test(){
            //读取配置文件
            ApplicationContext ctx=new ClassPathXmlApplicationContext("spring-config.xml");
            //获取bean的实例
            UserService userService =(UserService) ctx.getBean("userService");
            //调用方法
            userService.add();
        }
    }
    
    
    • 运行结果

    3)基于 注解 的依赖注入

    使用注解注入依赖对象不用再在代码中写依赖对象的setter方法或者该类的构造方法,并且不用再配置文件中配置大量的依赖对象,使代码更加简洁,清晰,易于维护。

    • Dao类
    //基于注解
    @Repository
    public class UserDao {
        @Value("王五")
        private String name;
        @Value("15")
        private int age;
    
        public void insert(){
            System.out.println("保存用户:"+age+"岁"+name);
        }
    }
    
    //等价于XML配置中的
    <bean id="userDao" class="org.example.dao.UserDao">
        <constructor-arg name="name" value="王五"></constructor-arg>
        <constructor-arg name="age" value="15"></constructor-arg>
    </bean>
    
    • Service类
    @Service
    public class UserService {
    
    //    @Autowired
    //    private UserDao userDao;
    
    //    @Autowired
    //    @Qualifier("userDao")
    //    private UserDao userDao;
    
        @Resource(name = "userDao")
        private UserDao userDao;
    
        public void add(){
            userDao.insert();
        }
    }
    
    //等价于XML配置中的
    <bean id="userService" class="org.example.service.UserService">
         <constructor-arg name="userDao" ref="userDao"></constructor-arg>
    </bean>
    

    以上三种写法都可

    • 测试类
        //基于注解
        @Autowired
        private UserService userService;
    
        @Test
        public  void  test1(){
            userService.add();
        }
    
    • 运行结果

    Spring中常见注解
    用于创建对象的注解

    注解的作用和 xml 配置文件中编写 <bean></bean> 标签的功能是一样的,即将当前类对象存入 Spring 容器中

    • @Componcnt注解:该注解用于把当前类对象存入 Spring 容器中。
      该注解的属性:
      value: 指定 bean 的 id。假如我们没有指定 value 的值,默认为当前类名的首字母改小写,其余不变的名称。
    • ②其中 Spring 还为我们提供了三个作用和 @Component 一样的注解(使得我们的三层对象更加清晰):
      @Controller 一般用在表现层
      @Service 一般用在业务层
      @Repository 一般用在持久层
    用于改变创建的对象的注入数据的注解
    • @Autowritred 注解:该注解可以自动按照类型注入数据到 bean 中。基于暴力反射原理,无需提供setter等方法
      注入条件:
      如果 ioc 容器中用唯一的一个 bean 对象类型和要被注入的变量类型匹配
      如果 ioc 容器中没有对应的 bean 对象类型和要被注入的变量类型匹配,那么会抛出异常。
      如果 ioc 容器中有多个 bean 对象类型和要被注入的变量类型匹配,首先会根据 id 来匹配,如果 id 都一样,则会根据要被注入的变量的名称匹配,如果变量的名称都一样,那么就会抛出异常。
    • @Qualifier 注解:该注解,在 @Autowritred 的基础之上,可以添加 value 属性值,指定注入的 bean id(需要和 @Autowritred 一起使用)。
    • @Resource 该注解和 @Qualifier 类似,可以指定注入的 bean id,但不需要和 @Autowritred 一起使用。@Resource 单独使用。
    • @Value 注解:该注解用于注入基本数据类型和 String 数据类型
      该注解的属性:
      value:用于指定数据的值。可以是 Spring 中的 el 表达式(SpEL)
      SpEL 书写格式:@Value("${xxxx}")
      说明:
      @Autowritred @Qualifier @Resource 只能注入其他 bean 类型的数据,不能注入基本数据类型和 String 类型的数据。
      集合类型的数据只能通过 XML 文件进行注入。
    用于改变创建的对象的作用范围的注解
    • @Scope 注解:该注解用于指定 Bean 的作用范围
      该注解的属性:
      value:指定范围的取值。吃常用的取值有:singleton(单例的)、prototype(多例的)
      默认为单例的(对象只创建一次)
    生命周期相关的注解
    • @PreDestroy 销毁方法的注解
    • @PostConstruct 初始化方法的注解
    Spring的新注解
    • @Configuration 注解: 用于指定当前类是一个 Spring 配置类,当创建容器时会从该类上加载注解。 获取容器时需要使用AnnotationApplicationContext(有@Configuration 注解的类.class)。
    // 如果加载spring-context.xml文件:
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
    
    // @Configuration注解的spring容器加载方式,用AnnotationConfigApplicationContext替换ClassPathXmlApplicationContext
    ApplicationContext context = new AnnotationConfigApplicationContext(TestConfiguration.class);
    
    • @ComponentScan 注解: 用于指定 spring 在初始化容器时要扫描的包。作用和在 spring 的 xml 配置文件中的:<context:component-scan base-package="包扫描的路径"/> 是一样的
      该注解的属性:
      basePackages:用于指定要扫描的包。
      value:用于指定要扫描的包。功能和 basePackages 属性功能一样。
    • @Bean 注解:该注解只能写在方法上,表明使用此方法创建一个对象,并且放入 spring 容器。
      该注解的属性:
      name:给当前 @Bean 注解方法创建的对象指定一个名称 (即 bean 的 id)。
    • @PropertySource注解:用于加载 .properties 文件中的配置。 例如我们配置数据源时, 可以把连接数据库的信息写到properties 配置文件中,就可以使用此注解指定 properties 配置文件的位置。
      该注解的属性:
      value[]:用于指定 properties 文件位置。如果是在类路径下,需要写上 classpath:
    @PropertySource(value={"classpath:jdbc.properties"})
    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 password;
     
        @Bean(name="dataSource")
        public DataSource createDataSource() throws PropertyVetoException {
            ComboPooledDataSource dataSource = new ComboPooledDataSource();
            dataSource.setDriverClass(driver);
            dataSource.setJdbcUrl(url);
            dataSource.setUser(username);
            dataSource.setPassword(password);
            return dataSource;
        }
    }
    
    • @Import 注解:此时我们已经有了两个配置类(SpringConfiguration、JdbcConfig),但是他们还没有关系。下面我们可以使用 @Import 注解,为这两个配置类建立关系链接。
      用于导入其他配置类,在引入其他配置类时,可以不用再写@Configuration 注解。当然,写上也没问题。
      该注解的属性:
      value[]:用于指定其他配置类的字节码。
    @Configuration
    @ComponentScan(basePackages="pers.stringbug")
    @PropertySource(value={"classpath:jdbc.properties"})
    @Import(value={JdbcConfig.class})
    public class SpringConfiguration {
        /**
         * 创建 QueryRunner 对象,并存入 Spring 容器中
         * 其中 @Qualifier(value="dataSource") 指定 bean 参数 id
         */
        @Bean(name="runner")
        public QueryRunner createQueryRunner(@Qualifier(value="dataSource") DataSource dataSource) {
            return new QueryRunner(dataSource);
        }
    }
    

    3.1.2、依赖注入的类型

    1)注入bean类型

    示例如上

    2)注入基本数据类型

    示例如上

    3)给List结构注入:List、Set集合,Array数组

    List集合需要使用<list>标签来配置注入
    Set集合需要使用<set>标签来配置注入,其配置参数及含义和<list>标签完全一样。
    Array数组需要使用<array>标签来配置注入,其配置参数及含义和<list>标签完全一样。

    • 实体类
    public class School {
        private List<String> studentList;
        private Set<String> studentSet;
        private String[] studentArray;
        //省略setter、getter方法
    }
    
    • XML文件
    <!--  测试setter注入复杂类型  -->
        <bean id="school" class="org.example.entity.School">
            <property name="studentList">
                <list>
                    <value>张三</value>
                    <value>李四</value>
                    <value>王五</value>
                </list>
            </property>
    
            <property name="studentSet">
                <set>
                    <value>张三</value>
                    <value>李四</value>
                    <value>王五</value>
                </set>
            </property>
    
            <property name="studentArray">
                <array>
                    <value>张三</value>
                    <value>李四</value>
                    <value>王五</value>
                </array>
            </property>
    </beans>
    
    • 测试类
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = "classpath:spring-config.xml")
    public class UserServiceTest {
        //测试基于setter注入复杂数据类型
        @Test
        public  void  test(){
            //读取配置文件
            ApplicationContext ctx=new ClassPathXmlApplicationContext("spring-config.xml");
            //获取bean的实例
            School school =(School) ctx.getBean("school");
            //List结构注入
            System.out.println(school.getStudentList());
            System.out.println(school.getStudentSet());
            System.out.println(Arrays.toString(school.getStudentArray()));
            //Map结构注入
    //        System.out.println(school.getStudentAgeMap());
    //        System.out.println(school.getStudentClassProperties());
        }
    }
    
    • 运行结果
    4)给Map结构注入:Map集合,Properties对象

    Map集合需要使用<map>标签来配置注入,其属性“key-type”和“value-type”分别指定“键”和“值”的数据类型。
    Properties对象需要使用<props>标签来配置注入,键和值类型必须是String,不能变,子标签<prop key=”键”>值</prop>来指定键值对。

    • 实体类
    public class School {
        private Map<String,Integer> studentAgeMap;
        private Properties studentClassProperties;
        //省略setter、getter方法
    }
    
    • XML文件
    <!--  测试setter注入复杂类型  -->
        <bean id="school" class="org.example.entity.School">
            <property name="studentAgeMap">
                <map>
                    <entry key="张三" value="18"></entry>
                    <entry key="王五">
                        <value>16</value>
                    </entry>
                </map>
            </property>
    
            <property name="studentClassProperties">
                <props>
                    <prop key="张三">初一三班</prop>
                    <prop key="李四">初二五班</prop>
                </props>
            </property>
        </bean>
    </beans>
    
    • 测试类
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = "classpath:spring-config.xml")
    public class UserServiceTest {
        //测试基于setter注入复杂数据类型
        @Test
        public  void  test(){
            //读取配置文件
            ApplicationContext ctx=new ClassPathXmlApplicationContext("spring-config.xml");
            //获取bean的实例
            School school =(School) ctx.getBean("school");
            //Map结构注入
            System.out.println(school.getStudentAgeMap());
            System.out.println(school.getStudentClassProperties());
        }
    }
    
    • 运行结果

    参考知乎作者「god23bin」的原创文章,原文链接:https://zhuanlan.zhihu.com/p/640517982
    参考CSDN博主「ADAMs.」的原创文章,原文链接:https://blog.csdn.net/qq_43097201/article/details/104486887
    参考博客园作者「LeeHua」的原创文章,原文链接:https://www.cnblogs.com/liyihua/p/14482488.html


    3.2、AOP

    简言之,就是在【编码时】把业务逻辑处理代码(例如:用户信息管理的业务逻辑功能)和额外的功能代码(日志记录,权限控制,事务处理等)【分离】开来,降低程序间的耦合度,使得软件的结构更加清晰

    在【运行时】,使用【动态代理技术】把业务逻辑处理和额外功能处理【结合】到一块,既能完成业务逻辑处理,也能完成额外的功能。并且额外功能逻辑可以多次重复使用,减少了重复代码的开发,提高了程序的复用性

    实现方式:

    实现方式

    实现方式

    实现方式

    动态代理

    3.2.1、JDK动态代理

    3.2.2、CGLIB动态代理


    4、Mybatis


    5、SpringMVC

    相关文章

      网友评论

          本文标题:02-SSM框架开发

          本文链接:https://www.haomeiwen.com/subject/npkfndtx.html