神刀安全网

Spring学习笔记

概述

Spring是什么?

  • Spring是一个开源框架,为了解决企业应用开发的复杂性而创建的,但是现在已经不止于企业应用
  • 是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架
    1. 从大小和开销两方面而言Spring都是轻量的
    2. 通过控制反转可以达到松耦合的目的
    3. 提供面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务进行内聚性的开发
    4. 包含并管理应用对象的配置和生命周期,这个意义是一种容器
    5. 将简单的组件配置、组合成为复杂的应用,这个意义上讲,他也是一种框架

为什么是Spring

  • 在Spring上开发应用简单
  • 在Spring上开发应用方便
  • 在Spring上开发应用快捷
    Spring带来了复杂的JavaEE开发的春天

Spring的作用

  • 容器

  • 提供了对多种技术的支持

    1. JMS
    2. MQ支持
    3. UnitTest
  • AOP(事务管理,日志等)

  • 提供众多方便应用的辅助类(JDBC Template等)

  • 对主流框架提供了良好的支持

适用范围

  • 构建企业应用(SSM-SSH)
  • 单独使用Bean容器(Bean管理)
  • 单独使用AOP进行面向切面编程
  • 其他的Spring的应用比如对消息的支持
  • 在互联网中的应用等…

专题一 IOC

  • 接口和面向接口编程
  • 什么是IOC
  • Spring的Bean的配置
  • Bean的初始化
  • Spring的常用注入方式

接口

  • 用于沟通的中介物的抽象化
  • 实体把自己提供给外界的功能进行抽象说明
  • Java中的接口就是说明,说明那些方法是对外公开的
  • 在Java8中接口可以拥有方法体

面向接口编程

  • 结构设计中,分清层次和调用关系,每层只向外提供一组功能接口,各层次之间依赖接口而不是实现类
  • 接口实现的变动,不影响各层之间的调用,这一点在公共服务尤其重要
  • “面向接口编程”中的“接口”是用于隐藏具体的实现是实现多态性的组件

什么是IoC

  • IoC:控制反转,控制权的转移,应用程序本身不负责依赖对象的创建和维护,而是由外部容器来负责对象的创建和维护
  • DI:依赖注入是IoC的一种实现方式
  • 目的:创建对象并组装对象之间的关系

Bean容器初始化

  • 基础:两个包
    1. org.springframework.beans
    2. org.springframework.context
  • 方式,ApplicationContext
    1. 本地文件
    2. classpath
    3. Web应用中依赖servlet或Listener

Spring注入

  • Spring注入是指在启动spring容器加载bean配置的时候,完成对变量的赋值行为
  • 常用的两种注入方式:
    1. 设值注入
    2. 构造注入

专题二 Bean

  • Bean的配置项
  • Bean的作用域
  • Bean的生命周期
  • Bean的自动装配
  • Resources & ResourcesLoader–>IoC加载的资源文件

Bean的配置项

  • Id–>在整个IoC容器中Bean的唯一表示
  • Class –> Bean的资源路径
  • Scope –> 作用域
  • Constructor arguments –> 构造器的属性
  • Properties –> 属性
  • Autowiring mode –> 自动注入模式
  • lazy-initialzation mode –> 懒加载模式
  • Initization/destruction mothod –> 初始化和销毁的方法

Bean的作用域

  • singleton:单例,指在一个bean容器中只存在一份
  • prototype:每次请求都会创建一个新的实例,destroy方式不生效
  • request:每次http请求都会创建一个实例且仅在当前的request有效
  • session:同上,每次http请求创建一个实例,当前session有效
  • global session:基于portlet的web中有效,如果是在web中 同session

Bean的生命周期

  • 定义

  • 初始化

  • 使用

  • 销毁

初始化:
  1. 实现org.springframework.beans.factory.InitalizingBean接口,配置afterPropertiesSet方法
  2. 配置init-mothod方法
  3. 配置全局默认的初始化销毁方法:在xml文件中的beans中配置default-init-mothod和default-destory-mothod
销毁:
  1. 实现org.springframework.beans.factory.DisposableBean接口,配置destroy方法
  2. 配置destroy-mothod方法–> clearup()方法
内部的,接口的,默认的三种初始化和销毁方法的优先级:
  1. 如果配置了init-mothod/destroy-mothod 或者实现了接口那么默认的default-init-mothod和default-destory-mothod就会变得无效
  2. 如果同时配置了init-mothod/destroy-mothod 和实现了接口,接口的优先级更高,接口的方法先执行

Aware接口

  • Spring中提供了以Aware结尾的接口,实现了Aware接口的bean在初始化之后,可以获取相应资源
  • 通过Aware接口,可以对Spring相应资源进行操作(慎重操作)
  • 为对Spring的简单扩展提供了方便的入口
ApplicationContextAware:

when an ApplicationContext creates a class that implements the org.springframework.context.ApplicationContextAware interface, the class is provided with a reference to that ApplicationContext

BeanNameAware

Bean的自动装配(AutoAwaring)

default-autoAwaring:

  • No:不做任何操作
  • byName:根据属性名自动装配,此选项将检查容器并根据名字查找与属性完全一直的bean,并将其自动装配
  • byType:如果容器中存在一个与属性类型相同的bean,那么将其自动装配;如果有多个则抛出异常
  • Constructor:与byType相同,不同之处在于它应用构造器参数

Bean装配之Resources

  • 针对资源文件的同意接口

  • Resources:

    1. UrlResources:URL对应的资源,根据一个URL地址即可构建
    2. ClassPathResource:获取类路径下的资源文件
    3. FileSystemResource:获取文件系统里的资源
    4. ServletContextResource:ServletContext封装的资源。用于访问ServletContext环境下的资源
    5. InputStreamResource:针对输入流封装的资源
    6. ByteArrayResource:针对字节数组封装的资源
  • ResourceLoader: All application contexts implement the ResourceLoader interface, and therefore all application context may be used to obtain Resource interfaces.

    1. classpath: Loaded from the classpath –> classpath:com/myapp/config.xml
    2. file: Loaded as a URL, from the filesystem –> file:/data/config.xml
    3. http: Loaded as a URL –> http:/myserver/logo.png
    4. (none): Depends on the application context —> data/config.xml

Bean的定义以及作用于的注解实现

  • Classpath 扫描与组件管理
  • 类的自动检测和注册Bean
  • <context: annotation-config/>
  • @Component, @Repository, @Service, @Controller
  • @Required
  • @Autowired
  • @Qualifier
  • @Resource
Classpath 扫描与组件管理
  • 从Spring3.0开始,Spring JavaConfig项目提供了很多特征,包括使用java而不是XML的形式来定义Bean,比如@Configuration, @Bean, @Import, @DependsOn
  • @Component是一个通用注解,可以用于任何bean
  • @Repository, @Service, @Controller是更有针对性的注解
    1. @Repository通常用于注解DAO层,即数据持久层
    2. @Service通常用于注解Service层,即服务层
    3. @Controller通常用于注解Controller层,即控制层(MVC)
元注解(Meta-annotations)
  • 遇到Spring 提供的注解可以作为自己代码的一部分,即“元数据注解”,元注解是一个简单的注解一个应用到另一个注解比如@Component注解是@service注解的元注解
  • 除了value(),元注解元注解还可以有其他属性,允许定制
类的自动检测及bean的注册
  • Spring可以自动检测类并注册Bean到ApplicationContext中
<context: annotation-config/>
  • 通过在基于XML的Spring配置如下标签
  • <context: annotation-config/>仅会查找在同一个applicationContext中的bean注解
类的自动检测及Bean的注册
  • 为了能够检测这些类并注册相应的Bean,需要如下内容:

    <context:component-scan base-package="org.example"/> 
  • <context:component-scan> 包含<context:annotation-config>,通常在使用前者后,不会使用后者

  • AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor也会被包含进来

使用过滤器进行自定义扫描
  • 默认情况下类被发现并注册bean的条件是:使用@Component,@Reponsitory,@service,@Controller注解或者使用@Component的自定义注解
  • 可以通过过滤器修改上面的行为:如在 <context:compoent-scan base-package="org.example"> </context:compoent-scan> 终结添加 <context:include-filter><context:exclude-filter> 来通过过滤器修改上面的行为
  • 还可以使用use-default-filters=”false” 禁用自动发现并注册
定义Bean
  • 扫描过程中被自动监测,那么Bean名称是由BeanNameGenerator生成的(@Component, @Repository, @Service, @Controller都会有个name属性可以用来显示的设置Bean Name)
  • 可自定义命名策略,实现BeanNameGenerator接口,并一定要包含一个无参的构造器
作用域
  • 通常情况下自动查找的Spring组件,其scope是singleton,Spring2.5提供了一个标识scope的注解@Scope
  • 也可以自定义scope策略,实现ScopeMetadataResolve接口并提供一个无参构造器

Spring Bean装配注解说明

@Required
  • @Required注解适用于bean属性的setter方法
  • 这个注解仅仅表示,受影响的bean属性必须在配置的时候被填充,通过在bean定义或通过自动装配一个明确的属性值

@Autowried

  • 可以将@Autowried注解为“传统”的setter方法
  • 可用于构造器和成员变量
  • 默认情况下,如果找不到合适的bean将会导致autowiring失败抛出异常,可以通过 @Autowired(required=false) 避免
  • 每个类只有一个构造器被标记为required=true
  • @Autowried的必要属性,建议使用@Required注解
  • 可以使用@Autowried注解那些众所周知的解析依赖接口,比如BeanFactory,ApplicationContext,Environment,ResourceLoader,ApplicationEventPublisher,and MessageSource
  • 可以通过添加注解给需要该类型的数组的字段和方法,以提供ApplicationContext中的所有特定类型的bean
  • 可以用于装配key为String类型的Map
  • 如果希望数组有序,可以让bean实现org.springframework.core.Ordered接口或者使用的@Order注解
  • @Autowried室友spring BeanPostProcessor处理的,所以不能再自定义的BeanPostProcessor类型应用@Autowried注解,这个类型必须通过XML或者Spring的@Bean注解加载

@Qualifier

  • 按类型自动转配可能多个bean实例的情况下,可以使用String的@Qualifier注解缩小范围,也可以用于指定唯一的@Qualifier注解缩小范围(或者指定唯一),也可以用于指定单独的构造器参数或者方法参数
  • 可用于注解集合类型变量

基于java的容器注解

@Bean
  • @Bean标识一个用于配置和初始化一个由SpringIoC容器管理的新对象的方法,类似于XML配置文件的<bean/>
  • 可以再Spring的@Component注解的类中使用@Bean注解任何方法(仅仅是可以)
  • 上一点中,通常使用的是@Configuration //这样的类相当于一个XML配置文件
  • 可以支持自定义Bean name
  • init-mothod 和 destroy-method
@ImportResource和@Value
  • 首先在xml中配置 <context:property-placeholder location="classpath:/config.properties">
  • 第二步 @Configuration注解的类同时使用 @ImportResource注解引用资源文件,在属性中注入的时候使用@Value("${jdbc.url}")的方式赋值
@Bean和@Scope
  • 默认@Bean是单例的,Bean的作用域包括singleton、prototype、request、session、global session
  • @Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)

基于泛型的自动装配

  • spring4中新加入的
  • spi

Spring的JSR250标准的支持

@Resource
  • Spring还支持使用JSR-250@Resource注解的变量或setter方法,还是一种支持JavaEE 5 和 6的通用注解,Spring管理的对象也支持这种模式
  • @Resource有一个name的属性,并且默认情况下Spring解释该值作为被注入的bean的名称
  • 如果没有显示的指出@Resource的name,默认的名称是从属性名或者setter方法得出
  • 注解提供的名字解析为一个bean的名称,这是由ApplicationContext中的CommonAnnotationBeanPostProcessor发现并处理的
@PostConstruct and @PreDestroy
  • CommonAnnotationBeanPostProcessor不仅能识别JRS-250中的生命周期注解@Resource,在Spring2.5中引入支持初始化和销毁回调,前提是CommonAnnotationBeanPostProcessor是Spring的ApplicationContext中注册的

专题三AOP

  • 什么是AOP及实现方式
  • AOP基本概念
  • Spring中的AOP
  • Schema-based AOP
  • Spring AOP API
  • AspectJ

什么是AOP

  • AOP:Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术
  • 主要的功能:日志记录,性能统计,安全控制,事务处理,异常处理等…
  • 切面是和功能垂直的,每个功能都会使用到切面,业务功能是横向的
AOP实现方式
  • 预编译:AspectJ
  • 运行期动态代理(JDK动态代理、CGLib动态代理):SpringAOP、JbossAOP
AOP相关的概念:
  • 切面(Aspect):一个关注点的模块化,这个关注点可能会横切多个对象
  • 连接点(JoinPoint):程序执行过程中的某个特定的点
  • 通知(Advice):在切面的某个特定的连接点上进行的动作
  • 切入点(Pointcut):匹配连接点的断言,在一个AOP中通知和一个切入点表达式关联
  • 引入(Introduction):在不修改类代码的前提下,为类添加新的方法和属性
  • 目标对象(Target object) 被一个或者多个切面所通知的对象
  • AOP代理(AOP Proxy):AOP框架创建的对象,用来实现切面契约(aspect contract)(包括通知方法执行等功能)
  • 织入(Weaving):把切面连接到其他的应用程序类型或者对象上,并创建一个被通知的对象,分为:编译时织入、类加载时织入、执行时织入
通知(Advice)
  • 前置通知(Before advice):在某个连接点之前执行的通知,但不能阻止连接点之前的执行
  • 返回后通知(After returning advice):在某连接点正常完成后执行的通知
  • 抛出异常后通知(After throw advice):在方法执行抛出异常之后执行的通知
  • 后通知(After (finally) advice):当某连接点退出的时候执行的通知(不论正常退出还是抛出异常)
  • 环绕通知(Around advice):包围一个连接点的通知
Spring框架中AOP的用途
  • 提供了声明式的企业服务,特别是EJB的代替服务的声明
  • 允许用户定制自己的方面,以完成OOP和AOP的互补使用
Spring的AOP实现
  • 纯java实现,无需特殊的编译过程,不需要控制类加载器层次
  • 目前只支持方法执行连接点(通知Spring Bean的方法执行)
  • 不是为了提供最完整的AOP实现,而是侧重于提供一种AOP实现和Spring IoC容器之间的整合,用于帮助解决应用中的常见问题
  • SpringAOP不会与AspectJ竞争,从而提供综合全面的AOP解决方案
有接口和无接口的Spring AOP实现区别
  • 业务对象实现接口的:Spring AOP默认使用标准的JavaSE动态代理作为AOP代理,这使得任何接口都可以被代理
  • 业务对象没有实现接口的:Spring AOP中也可以使用CGLIB代理

配置切面aspect

Schema-based AOP

Spring所有的切面和通知器都必须放在一个<aop:config> 内(可以配置多个包含<aop:config> 元素),每一个<aop:config> 可以包含pointcut,advisor和aspect元素(他们必须按照顺序声明)

<aop:config> 风格的配置大量使用了Spring的自动代理机制

aspect
    <bean id="aspectBiz" class="AspectBiz"></bean>     <bean id="moocAspect" class="MoocAspect"></bean>     <aop:config>         <aop:aspect id="moocAspectAOP" ref="moocAspect">         </aop:aspect>     </aop:config> 

配置切入点Pointcut

pointcut
  • execution(public * *(..)) 切入点为执行所有public方法时
  • execution(* set *(..)) 切入点为执行所有set方法时
  • execution(* com.xyz.service.AccountService.*(..)) 切入点为执行AccountService类中所有的方法时
  • execution(* com.xyz.service..(..)) 切入点为执行service包下的所有方法时
  • execution(* com.xyz.service…(..)) 切入点为执行service包及其子包下面的所有方法时

下面为仅spring支持的:

  • within(com.xyz.service.*)
  • within(com.xyz.service..*) within用来指定类型内的方法执行
  • this(com.xyz.service.AccountService) this是用来匹配当前AOP代理对象类型的执行方法
  • target
  • @args()…

Advice应用通知

Before advice

<aop:before pointcut-ref="" mothod=""/> 在匹配到的方法之前进行通知,执行通知方法

After returning advice

<aop:after-returning method="" pointcut-ref=""/> 在匹配的方法返回之后进行通知,执行通知方法

After throw advice

<aop:after-throwing method="" pointcut-ref=""/> 在匹配的方法抛出异常之后进行通知,执行通知方法

after advice

<aop:after method="" pointcut-ref=""/> 在匹配的方法抛出异常或者正常返回之后进行通知,必然执行,并且永远是最后一个执行的

Around advice 环绕通知

通知方法的第一个参数必须是ProceedingJoinPoint类型,在ProceedingJoinPoint的proceed方法前后,都可以执行通知和相应的逻辑。可以传入参数

Introductions

  • 简介允许一个切面声明一个实现指定接口的通知对象,并且提供一个接口实现类来代表这些对象
  • <aop:aspect> 中的<aop:declare-parents> 元素声明该元素用于声明所匹配的类型拥有一个新的parent(因此而得名)
<aop:config>         <aop:aspect id="moocAspectAOP" ref="moocAspect">             <aop:declare-parents types-matching="com.xyz.myapp.service.*"                              implement-interface="com.xyz.myapp.service.tracking.UsageTracked"                              default-impl="com.xyz.myapp.service.tracking.defaultUsageTracked"/>             <aop:before method="recordUsage" pointcut="com.xyz.myapp.SystemArchitecture.businessService()                              and this(usageTracked)"/>         </aop:aspect>     </aop:config> 
  • 所有支持配置文件的schema-defined aspects只支持singleton model也就是单例模式

Advisors

  • advisor就像一个小的自包含的aspect,只有一个advice
  • 切面自身通过bean表示,并且必须实现某个advice接口,同时,advisor也可以很好的利用AspectJ的切入点表达式
  • Spring通过配置文件中 <aop:advisor> 元素支持advisor实际使用中,大多数情况下它会和transactional advice配合使用
  • 为了定义一个advisor的优先以便让advice可以有序,可以使用order属性来定义advisor的顺序
    <aop:config>         <aop:pointcut id="businessService" expression="execution(* com.xyz.myapp.service..(..))"/>         <aop:advisor advice-ref="tx-advice" pointcut-ref="businessService"/>     </aop:config>     <tx:advice id="tx-advice">         <tx:attrbutes>             <tx:mothod name = "" propagation = ""/>         </tx:attrbutes>     </tx:advice> 

专题四 Spring AOP API

  • 这是Spring1.2的历史用法,现在仍然支持
  • 这是SpringAOP的基础,不得不了解
  • 现在的用法也是基于历史的,只是更加的简便了

Pointcut

  • 实现之一:NameMatchMethodPointcut,根据方法的名字进行匹配
  • 成员变量:mappedNames,匹配的方法名的集合
<bean id="pointcutBean" class="org.springframework.aop.support.NameMatchMethodPointcut">         <property name="mappedNames">             <list>                 <value>sa*</value>             </list>         </property>     </bean> 
Before advice
  • 一个简单的通知类型
  • 只是在进入方法之前被调用,不需要MethodInvocation对象
  • 前置通知可以在连接点执行之前插入自定义的行为,但不能改变返回值
Throws advice
  • 如果连接点抛出异常,throws advice在连接点返回之后 被调用
  • 如果throws-advice的方法抛出异常,那么它将覆盖原有的异常
  • 接口org.springframework.aop.ThrowsAdvice不包含任何方法,仅仅只是一个声明,实现类需要实现类似于下面的方法:void afterThrowing([Method, args, target], ThrowableSubClass);
After Returning advice
  • 后置通知必须实现org.springframework.aop.AfterReturningAdvice接口
  • 可以访问返回值(但是不能修改)、被调用的方法、方法 的参数或者目标
  • 如果抛出异常,将会抛出拦截器链,替代返回值
Interception around advice
  • Spring的切入点模型使得切入点可以独立于advice重用,针对不同的advice可以使用相同的切入点
  • 实现org.springframework.aop.MethodInterceptor接口
Introduction advice
  • Spring把引入通知作为一种特殊的拦截通知
  • 需要IntroductionAdvisor和IntroductionInterceptor
  • 仅适用与类,不能和任何切入点一起使用
  • introduction advisor比较简单,持有一个独立的LockMixin实例
advisor API in Spring
  • Advisor是仅包含一个切入点表达式关联的单个通知的方面
  • 除了introductions, advisor可以用于任何通知
  • org.springframework.aop.support.DefaultPointcutAdvisor是最常用的advisor类,它可以与MethodInterceptor,BeforeAdvice或者ThrowsAdvice一起使用
  • 它可以混合在Spring同一个AOP代理的advisor和advice

ProxyFactoryBean

  • 创建Spring AOP代理的基本方法是使用org.springframework.aop.framework.ProxyFactoryBean
  • 这可以完全控制切入点和通知(advice)以及他们的顺序

定义一个foo的bean id的对象引用,这个引用的对象是ProxyFactoryBean实现里getObject()方法创建的对象

getObject方法将创建一个AOP代理包装一个目标对象

ProxyFactoryBean
  • 使用ProxyFactoryBean或者其他IoC相关类来创建AOP代理的最重要的好处是通知和切入点也可以使用IoC来管理
  • 被代理类没有实现任何的接口,使用CGLiB代理,否则使用JDK代理
  • 通过设置ProxyTargetClass为true,可强制使用CGLIB
  • 如果目标类实现了一个或者多个接口,那么创建代理的类型将依赖ProxyFactoryBean的配置
  • 如果ProxyFactoryBean的proxyInterfaces属性被设置为一个或者多个全限定接口名,基于JDK的代理将被创建

Proxying classes

  • 如果没有接口,这种情况下使用的是CGLIB的代理,而不是JDK的动态代理
  • 如果想,可以强制使用CGLIB的代理,即使有接口
  • CGLIB代理的工作原理实在运行时生成目标类的子类,Spring配置这个生成的子类委托方法调用到原来的目标
  • 子类是用来实现Decorator模式,织入通知
使用global advisors
  • 使用*最通配符,匹配所有拦截器加入通知链

专题五 Spring 对 AspectJ的支持

AspectJ

  • @AspectJ的风格类似于纯java注解的不同java类
  • Spring可以使用AspectJ来作为切入点解析
  • AOP的运行时仍然是纯的Spring AOP,对AspectJ的编译器或者织入无依赖
Spring中配置AspectJ
  • 对@AspectJ支持使用XML或者Java风格的配置
  • 确保AspectJ的aspectjweaver.jar包含在应用程序的classpath中
@Configuration @EnableAspectJAutoProxy public class Appconfig() {      } 
<aop:aspectj-autoproxy/> 
aspect
  • @AspectJ切面使用@Aspect注解配置,拥有@Aspect的任何bean将被Spring自动识别并应用

  • 用@Aspect注解的类可以有方法和字段,他们也可能包含切入点pointcut,通知advice和引入introduction声明

  • @Aspect注解是不可以通过类路径自动检测发现的,需要配合使用@Component注解或者在XML文件中配置bean

  • 一个类中的@Aspect注解标识她是一个切面,并且将自己从自动代理中排除

pointcut
  • 一个切入点通过一个不同的方法定义来提供,并且切入点表达式使用@Pointcut注解,方法的返回值必须是void
组合切入点表达式

可以使用&& || !来组合切入点

定义良好的pointcuts
  • AspectJ是编译期的AOP
  • 检查代码并匹配连接点和切入点的代价是昂贵的
  • 一个好的切入点应该包含如下几点:
    • 选择特定类型的连接点:execution,get,set,call,handler
    • 确定连接点的范围,如:within withincode
    • 匹配上下文信息,如:this,target,@annotation

Advice

before advice

使用@before(“execution(* com.asd.fg.ao…)”)

after returning

使用@AfterReturning(“execution(* com.asd.fg.ao…)”),可以使用returning来绑定返回值

after throwing advice

使用@AfterThrowing(“execution(* com.asd.fg.ao…)”),可以使用returning来绑定返回值

after(finally) advice
  • 最终通知必须处理正常和异常的两种情况,它通常用于释放资源
  • 使用@After(“execution(* com.asd.fg.ao…)”),可以使用returning来绑定返回值
around advice
  • 环绕通知使用@Around注解来声明,通知方法的第一个参数必须是ProceedingJoinPoint类型
  • 在通知内部调用ProceedingJoinPoint的proceed()方法会导致执行真正的方法,传入一个Object[]对象,数组中的值将会被作为参数传递个方法。

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » Spring学习笔记

分享到:更多 ()